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}