wasmer_vm/
sig_registry.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// This file contains code from external sources.
// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md

//! Implement a registry of function signatures, for fast indirect call
//! signature checking.

use std::collections::{hash_map, HashMap};
use std::convert::TryFrom;
use wasmer_types::{FunctionType, FunctionTypeRef};

/// An index into the shared signature registry, usable for checking signatures
/// at indirect calls.
#[repr(C)]
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
pub struct VMSharedSignatureIndex(u32);

impl VMSharedSignatureIndex {
    /// Create a new `VMSharedSignatureIndex`.
    pub fn new(value: u32) -> Self {
        Self(value)
    }
}

/// WebAssembly requires that the caller and callee signatures in an indirect
/// call must match. To implement this efficiently, keep a registry of all
/// signatures, shared by all instances, so that call sites can just do an
/// index comparison.
#[derive(Debug)]
pub struct SignatureRegistry {
    type_to_index: HashMap<FunctionType, VMSharedSignatureIndex>,
    index_to_data: Vec<FunctionType>,
}

impl SignatureRegistry {
    /// Create a new `SignatureRegistry`.
    pub fn new() -> Self {
        Self {
            type_to_index: HashMap::new(),
            index_to_data: Vec::new(),
        }
    }

    /// Register a signature and return its unique index.
    pub fn register(&mut self, sig: FunctionTypeRef<'_>) -> VMSharedSignatureIndex {
        let len = self.index_to_data.len();
        // TODO(0-copy): this. should. not. allocate.
        //
        // This is pretty hard to avoid, however. In order to implement bijective map, we'd want
        // a `Rc<FunctionType>`, but indexing into a map keyed by `Rc<FunctionType>` with
        // `FunctionTypeRef` is… not possible given the current API either.
        //
        // Consider `transmute` or `hashbrown`'s raw_entry.
        let sig = FunctionType::new(sig.params(), sig.results());
        match self.type_to_index.entry(sig.clone()) {
            hash_map::Entry::Occupied(entry) => *entry.get(),
            hash_map::Entry::Vacant(entry) => {
                debug_assert!(
                    u32::try_from(len).is_ok(),
                    "invariant: can't have more than 2³²-1 signatures!"
                );
                let sig_id = VMSharedSignatureIndex::new(u32::try_from(len).unwrap());
                entry.insert(sig_id);
                self.index_to_data.push(sig);
                sig_id
            }
        }
    }

    /// Looks up a shared signature index within this registry.
    ///
    /// Note that for this operation to be semantically correct the `idx` must
    /// have previously come from a call to `register` of this same object.
    pub fn lookup(&self, idx: VMSharedSignatureIndex) -> Option<&FunctionType> {
        self.index_to_data.get(idx.0 as usize)
    }
}