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)
}
}