wasmer_vm/
sig_registry.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md
3
4//! Implement a registry of function signatures, for fast indirect call
5//! signature checking.
6
7use std::collections::{hash_map, HashMap};
8use std::convert::TryFrom;
9use wasmer_types::{FunctionType, FunctionTypeRef};
10
11/// An index into the shared signature registry, usable for checking signatures
12/// at indirect calls.
13#[repr(C)]
14#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
15pub struct VMSharedSignatureIndex(u32);
16
17impl VMSharedSignatureIndex {
18    /// Create a new `VMSharedSignatureIndex`.
19    pub fn new(value: u32) -> Self {
20        Self(value)
21    }
22}
23
24/// WebAssembly requires that the caller and callee signatures in an indirect
25/// call must match. To implement this efficiently, keep a registry of all
26/// signatures, shared by all instances, so that call sites can just do an
27/// index comparison.
28#[derive(Debug)]
29pub struct SignatureRegistry {
30    type_to_index: HashMap<FunctionType, VMSharedSignatureIndex>,
31    index_to_data: Vec<FunctionType>,
32}
33
34impl SignatureRegistry {
35    /// Create a new `SignatureRegistry`.
36    pub fn new() -> Self {
37        Self {
38            type_to_index: HashMap::new(),
39            index_to_data: Vec::new(),
40        }
41    }
42
43    /// Register a signature and return its unique index.
44    pub fn register(&mut self, sig: FunctionTypeRef<'_>) -> VMSharedSignatureIndex {
45        let len = self.index_to_data.len();
46        // TODO(0-copy): this. should. not. allocate.
47        //
48        // This is pretty hard to avoid, however. In order to implement bijective map, we'd want
49        // a `Rc<FunctionType>`, but indexing into a map keyed by `Rc<FunctionType>` with
50        // `FunctionTypeRef` is… not possible given the current API either.
51        //
52        // Consider `transmute` or `hashbrown`'s raw_entry.
53        let sig = FunctionType::new(sig.params(), sig.results());
54        match self.type_to_index.entry(sig.clone()) {
55            hash_map::Entry::Occupied(entry) => *entry.get(),
56            hash_map::Entry::Vacant(entry) => {
57                debug_assert!(
58                    u32::try_from(len).is_ok(),
59                    "invariant: can't have more than 2³²-1 signatures!"
60                );
61                let sig_id = VMSharedSignatureIndex::new(u32::try_from(len).unwrap());
62                entry.insert(sig_id);
63                self.index_to_data.push(sig);
64                sig_id
65            }
66        }
67    }
68
69    /// Looks up a shared signature index within this registry.
70    ///
71    /// Note that for this operation to be semantically correct the `idx` must
72    /// have previously come from a call to `register` of this same object.
73    pub fn lookup(&self, idx: VMSharedSignatureIndex) -> Option<&FunctionType> {
74        self.index_to_data.get(idx.0 as usize)
75    }
76}