diff options
Diffstat (limited to 'third_party/rust/cranelift-wasm/src/environ')
-rw-r--r-- | third_party/rust/cranelift-wasm/src/environ/dummy.rs | 783 | ||||
-rw-r--r-- | third_party/rust/cranelift-wasm/src/environ/mod.rs | 11 | ||||
-rw-r--r-- | third_party/rust/cranelift-wasm/src/environ/spec.rs | 915 |
3 files changed, 1709 insertions, 0 deletions
diff --git a/third_party/rust/cranelift-wasm/src/environ/dummy.rs b/third_party/rust/cranelift-wasm/src/environ/dummy.rs new file mode 100644 index 0000000000..d0b59ee6ab --- /dev/null +++ b/third_party/rust/cranelift-wasm/src/environ/dummy.rs @@ -0,0 +1,783 @@ +//! "Dummy" implementations of `ModuleEnvironment` and `FuncEnvironment` for testing +//! wasm translation. For complete implementations of `ModuleEnvironment` and +//! `FuncEnvironment`, see [wasmtime-environ] in [Wasmtime]. +//! +//! [wasmtime-environ]: https://crates.io/crates/wasmtime-environ +//! [Wasmtime]: https://github.com/bytecodealliance/wasmtime + +use crate::environ::{ + FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment, + WasmFuncType, WasmResult, +}; +use crate::func_translator::FuncTranslator; +use crate::translation_utils::{ + DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, + Table, TableIndex, TypeIndex, +}; +use core::convert::TryFrom; +use cranelift_codegen::cursor::FuncCursor; +use cranelift_codegen::ir::immediates::{Offset32, Uimm64}; +use cranelift_codegen::ir::types::*; +use cranelift_codegen::ir::{self, InstBuilder}; +use cranelift_codegen::isa::TargetFrontendConfig; +use cranelift_entity::{EntityRef, PrimaryMap, SecondaryMap}; +use cranelift_frontend::FunctionBuilder; +use std::boxed::Box; +use std::string::String; +use std::vec::Vec; +use wasmparser::{FuncValidator, FunctionBody, ValidatorResources, WasmFeatures}; + +/// Compute a `ir::ExternalName` for a given wasm function index. +fn get_func_name(func_index: FuncIndex) -> ir::ExternalName { + ir::ExternalName::user(0, func_index.as_u32()) +} + +/// A collection of names under which a given entity is exported. +pub struct Exportable<T> { + /// A wasm entity. + pub entity: T, + + /// Names under which the entity is exported. + pub export_names: Vec<String>, +} + +impl<T> Exportable<T> { + pub fn new(entity: T) -> Self { + Self { + entity, + export_names: Vec::new(), + } + } +} + +/// The main state belonging to a `DummyEnvironment`. This is split out from +/// `DummyEnvironment` to allow it to be borrowed separately from the +/// `FuncTranslator` field. +pub struct DummyModuleInfo { + /// Target description relevant to frontends producing Cranelift IR. + config: TargetFrontendConfig, + + /// Signatures as provided by `declare_signature`. + pub signatures: PrimaryMap<TypeIndex, ir::Signature>, + + /// Module and field names of imported functions as provided by `declare_func_import`. + pub imported_funcs: Vec<(String, String)>, + + /// Module and field names of imported globals as provided by `declare_global_import`. + pub imported_globals: Vec<(String, String)>, + + /// Module and field names of imported tables as provided by `declare_table_import`. + pub imported_tables: Vec<(String, String)>, + + /// Module and field names of imported memories as provided by `declare_memory_import`. + pub imported_memories: Vec<(String, String)>, + + /// Functions, imported and local. + pub functions: PrimaryMap<FuncIndex, Exportable<TypeIndex>>, + + /// Function bodies. + pub function_bodies: PrimaryMap<DefinedFuncIndex, ir::Function>, + + /// Tables as provided by `declare_table`. + pub tables: PrimaryMap<TableIndex, Exportable<Table>>, + + /// Memories as provided by `declare_memory`. + pub memories: PrimaryMap<MemoryIndex, Exportable<Memory>>, + + /// Globals as provided by `declare_global`. + pub globals: PrimaryMap<GlobalIndex, Exportable<Global>>, + + /// The start function. + pub start_func: Option<FuncIndex>, +} + +impl DummyModuleInfo { + /// Creates a new `DummyModuleInfo` instance. + pub fn new(config: TargetFrontendConfig) -> Self { + Self { + config, + signatures: PrimaryMap::new(), + imported_funcs: Vec::new(), + imported_globals: Vec::new(), + imported_tables: Vec::new(), + imported_memories: Vec::new(), + functions: PrimaryMap::new(), + function_bodies: PrimaryMap::new(), + tables: PrimaryMap::new(), + memories: PrimaryMap::new(), + globals: PrimaryMap::new(), + start_func: None, + } + } +} + +/// This `ModuleEnvironment` implementation is a "naïve" one, doing essentially nothing and +/// emitting placeholders when forced to. Don't try to execute code translated for this +/// environment, essentially here for translation debug purposes. +pub struct DummyEnvironment { + /// Module information. + pub info: DummyModuleInfo, + + /// Function translation. + trans: FuncTranslator, + + /// Vector of wasm bytecode size for each function. + pub func_bytecode_sizes: Vec<usize>, + + /// How to return from functions. + return_mode: ReturnMode, + + /// Instructs to collect debug data during translation. + debug_info: bool, + + /// Name of the module from the wasm file. + pub module_name: Option<String>, + + /// Function names. + function_names: SecondaryMap<FuncIndex, String>, +} + +impl DummyEnvironment { + /// Creates a new `DummyEnvironment` instance. + pub fn new(config: TargetFrontendConfig, return_mode: ReturnMode, debug_info: bool) -> Self { + Self { + info: DummyModuleInfo::new(config), + trans: FuncTranslator::new(), + func_bytecode_sizes: Vec::new(), + return_mode, + debug_info, + module_name: None, + function_names: SecondaryMap::new(), + } + } + + /// Return a `DummyFuncEnvironment` for translating functions within this + /// `DummyEnvironment`. + pub fn func_env(&self) -> DummyFuncEnvironment { + DummyFuncEnvironment::new(&self.info, self.return_mode) + } + + fn get_func_type(&self, func_index: FuncIndex) -> TypeIndex { + self.info.functions[func_index].entity + } + + /// Return the number of imported functions within this `DummyEnvironment`. + pub fn get_num_func_imports(&self) -> usize { + self.info.imported_funcs.len() + } + + /// Return the name of the function, if a name for the function with + /// the corresponding index exists. + pub fn get_func_name(&self, func_index: FuncIndex) -> Option<&str> { + self.function_names.get(func_index).map(String::as_ref) + } +} + +/// The `FuncEnvironment` implementation for use by the `DummyEnvironment`. +pub struct DummyFuncEnvironment<'dummy_environment> { + pub mod_info: &'dummy_environment DummyModuleInfo, + + return_mode: ReturnMode, +} + +impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> { + pub fn new(mod_info: &'dummy_environment DummyModuleInfo, return_mode: ReturnMode) -> Self { + Self { + mod_info, + return_mode, + } + } + + // Create a signature for `sigidx` amended with a `vmctx` argument after the standard wasm + // arguments. + fn vmctx_sig(&self, sigidx: TypeIndex) -> ir::Signature { + let mut sig = self.mod_info.signatures[sigidx].clone(); + sig.params.push(ir::AbiParam::special( + self.pointer_type(), + ir::ArgumentPurpose::VMContext, + )); + sig + } + + fn reference_type(&self) -> ir::Type { + match self.pointer_type() { + ir::types::I32 => ir::types::R32, + ir::types::I64 => ir::types::R64, + _ => panic!("unsupported pointer type"), + } + } +} + +impl<'dummy_environment> TargetEnvironment for DummyFuncEnvironment<'dummy_environment> { + fn target_config(&self) -> TargetFrontendConfig { + self.mod_info.config + } +} + +impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environment> { + fn return_mode(&self) -> ReturnMode { + self.return_mode + } + + fn make_global( + &mut self, + func: &mut ir::Function, + index: GlobalIndex, + ) -> WasmResult<GlobalVariable> { + // Just create a dummy `vmctx` global. + let offset = i32::try_from((index.index() * 8) + 8).unwrap().into(); + let vmctx = func.create_global_value(ir::GlobalValueData::VMContext {}); + Ok(GlobalVariable::Memory { + gv: vmctx, + offset, + ty: self.mod_info.globals[index].entity.ty, + }) + } + + fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> WasmResult<ir::Heap> { + // Create a static heap whose base address is stored at `vmctx+0`. + let addr = func.create_global_value(ir::GlobalValueData::VMContext); + let gv = func.create_global_value(ir::GlobalValueData::Load { + base: addr, + offset: Offset32::new(0), + global_type: self.pointer_type(), + readonly: true, + }); + + Ok(func.create_heap(ir::HeapData { + base: gv, + min_size: 0.into(), + offset_guard_size: 0x8000_0000.into(), + style: ir::HeapStyle::Static { + bound: 0x1_0000_0000.into(), + }, + index_type: I32, + })) + } + + fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> WasmResult<ir::Table> { + // Create a table whose base address is stored at `vmctx+0`. + let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); + let base_gv = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: Offset32::new(0), + global_type: self.pointer_type(), + readonly: true, // when tables in wasm become "growable", revisit whether this can be readonly or not. + }); + let bound_gv = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: Offset32::new(0), + global_type: I32, + readonly: true, + }); + + Ok(func.create_table(ir::TableData { + base_gv, + min_size: Uimm64::new(0), + bound_gv, + element_size: Uimm64::from(u64::from(self.pointer_bytes()) * 2), + index_type: I32, + })) + } + + fn make_indirect_sig( + &mut self, + func: &mut ir::Function, + index: TypeIndex, + ) -> WasmResult<ir::SigRef> { + // A real implementation would probably change the calling convention and add `vmctx` and + // signature index arguments. + Ok(func.import_signature(self.vmctx_sig(index))) + } + + fn make_direct_func( + &mut self, + func: &mut ir::Function, + index: FuncIndex, + ) -> WasmResult<ir::FuncRef> { + let sigidx = self.mod_info.functions[index].entity; + // A real implementation would probably add a `vmctx` argument. + // And maybe attempt some signature de-duplication. + let signature = func.import_signature(self.vmctx_sig(sigidx)); + let name = get_func_name(index); + Ok(func.import_function(ir::ExtFuncData { + name, + signature, + colocated: false, + })) + } + + fn translate_call_indirect( + &mut self, + mut pos: FuncCursor, + _table_index: TableIndex, + _table: ir::Table, + _sig_index: TypeIndex, + sig_ref: ir::SigRef, + callee: ir::Value, + call_args: &[ir::Value], + ) -> WasmResult<ir::Inst> { + // Pass the current function's vmctx parameter on to the callee. + let vmctx = pos + .func + .special_param(ir::ArgumentPurpose::VMContext) + .expect("Missing vmctx parameter"); + + // The `callee` value is an index into a table of function pointers. + // Apparently, that table is stored at absolute address 0 in this dummy environment. + // TODO: Generate bounds checking code. + let ptr = self.pointer_type(); + let callee_offset = if ptr == I32 { + pos.ins().imul_imm(callee, 4) + } else { + let ext = pos.ins().uextend(I64, callee); + pos.ins().imul_imm(ext, 4) + }; + let mflags = ir::MemFlags::trusted(); + let func_ptr = pos.ins().load(ptr, mflags, callee_offset, 0); + + // Build a value list for the indirect call instruction containing the callee, call_args, + // and the vmctx parameter. + let mut args = ir::ValueList::default(); + args.push(func_ptr, &mut pos.func.dfg.value_lists); + args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists); + args.push(vmctx, &mut pos.func.dfg.value_lists); + + Ok(pos + .ins() + .CallIndirect(ir::Opcode::CallIndirect, INVALID, sig_ref, args) + .0) + } + + fn translate_call( + &mut self, + mut pos: FuncCursor, + _callee_index: FuncIndex, + callee: ir::FuncRef, + call_args: &[ir::Value], + ) -> WasmResult<ir::Inst> { + // Pass the current function's vmctx parameter on to the callee. + let vmctx = pos + .func + .special_param(ir::ArgumentPurpose::VMContext) + .expect("Missing vmctx parameter"); + + // Build a value list for the call instruction containing the call_args and the vmctx + // parameter. + let mut args = ir::ValueList::default(); + args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists); + args.push(vmctx, &mut pos.func.dfg.value_lists); + + Ok(pos.ins().Call(ir::Opcode::Call, INVALID, callee, args).0) + } + + fn translate_memory_grow( + &mut self, + mut pos: FuncCursor, + _index: MemoryIndex, + _heap: ir::Heap, + _val: ir::Value, + ) -> WasmResult<ir::Value> { + Ok(pos.ins().iconst(I32, -1)) + } + + fn translate_memory_size( + &mut self, + mut pos: FuncCursor, + _index: MemoryIndex, + _heap: ir::Heap, + ) -> WasmResult<ir::Value> { + Ok(pos.ins().iconst(I32, -1)) + } + + fn translate_memory_copy( + &mut self, + _pos: FuncCursor, + _src_index: MemoryIndex, + _src_heap: ir::Heap, + _dst_index: MemoryIndex, + _dst_heap: ir::Heap, + _dst: ir::Value, + _src: ir::Value, + _len: ir::Value, + ) -> WasmResult<()> { + Ok(()) + } + + fn translate_memory_fill( + &mut self, + _pos: FuncCursor, + _index: MemoryIndex, + _heap: ir::Heap, + _dst: ir::Value, + _val: ir::Value, + _len: ir::Value, + ) -> WasmResult<()> { + Ok(()) + } + + fn translate_memory_init( + &mut self, + _pos: FuncCursor, + _index: MemoryIndex, + _heap: ir::Heap, + _seg_index: u32, + _dst: ir::Value, + _src: ir::Value, + _len: ir::Value, + ) -> WasmResult<()> { + Ok(()) + } + + fn translate_data_drop(&mut self, _pos: FuncCursor, _seg_index: u32) -> WasmResult<()> { + Ok(()) + } + + fn translate_table_size( + &mut self, + mut pos: FuncCursor, + _index: TableIndex, + _table: ir::Table, + ) -> WasmResult<ir::Value> { + Ok(pos.ins().iconst(I32, -1)) + } + + fn translate_table_grow( + &mut self, + mut pos: FuncCursor, + _table_index: TableIndex, + _table: ir::Table, + _delta: ir::Value, + _init_value: ir::Value, + ) -> WasmResult<ir::Value> { + Ok(pos.ins().iconst(I32, -1)) + } + + fn translate_table_get( + &mut self, + builder: &mut FunctionBuilder, + _table_index: TableIndex, + _table: ir::Table, + _index: ir::Value, + ) -> WasmResult<ir::Value> { + Ok(builder.ins().null(self.reference_type())) + } + + fn translate_table_set( + &mut self, + _builder: &mut FunctionBuilder, + _table_index: TableIndex, + _table: ir::Table, + _value: ir::Value, + _index: ir::Value, + ) -> WasmResult<()> { + Ok(()) + } + + fn translate_table_copy( + &mut self, + _pos: FuncCursor, + _dst_index: TableIndex, + _dst_table: ir::Table, + _src_index: TableIndex, + _src_table: ir::Table, + _dst: ir::Value, + _src: ir::Value, + _len: ir::Value, + ) -> WasmResult<()> { + Ok(()) + } + + fn translate_table_fill( + &mut self, + _pos: FuncCursor, + _table_index: TableIndex, + _dst: ir::Value, + _val: ir::Value, + _len: ir::Value, + ) -> WasmResult<()> { + Ok(()) + } + + fn translate_table_init( + &mut self, + _pos: FuncCursor, + _seg_index: u32, + _table_index: TableIndex, + _table: ir::Table, + _dst: ir::Value, + _src: ir::Value, + _len: ir::Value, + ) -> WasmResult<()> { + Ok(()) + } + + fn translate_elem_drop(&mut self, _pos: FuncCursor, _seg_index: u32) -> WasmResult<()> { + Ok(()) + } + + fn translate_ref_func( + &mut self, + mut pos: FuncCursor, + _func_index: FuncIndex, + ) -> WasmResult<ir::Value> { + Ok(pos.ins().null(self.reference_type())) + } + + fn translate_custom_global_get( + &mut self, + mut pos: FuncCursor, + _global_index: GlobalIndex, + ) -> WasmResult<ir::Value> { + Ok(pos.ins().iconst(I32, -1)) + } + + fn translate_custom_global_set( + &mut self, + _pos: FuncCursor, + _global_index: GlobalIndex, + _val: ir::Value, + ) -> WasmResult<()> { + Ok(()) + } + + fn translate_atomic_wait( + &mut self, + mut pos: FuncCursor, + _index: MemoryIndex, + _heap: ir::Heap, + _addr: ir::Value, + _expected: ir::Value, + _timeout: ir::Value, + ) -> WasmResult<ir::Value> { + Ok(pos.ins().iconst(I32, -1)) + } + + fn translate_atomic_notify( + &mut self, + mut pos: FuncCursor, + _index: MemoryIndex, + _heap: ir::Heap, + _addr: ir::Value, + _count: ir::Value, + ) -> WasmResult<ir::Value> { + Ok(pos.ins().iconst(I32, 0)) + } +} + +impl TargetEnvironment for DummyEnvironment { + fn target_config(&self) -> TargetFrontendConfig { + self.info.config + } +} + +impl<'data> ModuleEnvironment<'data> for DummyEnvironment { + fn declare_type_func(&mut self, _wasm: WasmFuncType, sig: ir::Signature) -> WasmResult<()> { + self.info.signatures.push(sig); + Ok(()) + } + + fn declare_func_import( + &mut self, + index: TypeIndex, + module: &'data str, + field: &'data str, + ) -> WasmResult<()> { + assert_eq!( + self.info.functions.len(), + self.info.imported_funcs.len(), + "Imported functions must be declared first" + ); + self.info.functions.push(Exportable::new(index)); + self.info + .imported_funcs + .push((String::from(module), String::from(field))); + Ok(()) + } + + fn declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()> { + self.info.functions.push(Exportable::new(index)); + Ok(()) + } + + fn declare_global(&mut self, global: Global) -> WasmResult<()> { + self.info.globals.push(Exportable::new(global)); + Ok(()) + } + + fn declare_global_import( + &mut self, + global: Global, + module: &'data str, + field: &'data str, + ) -> WasmResult<()> { + self.info.globals.push(Exportable::new(global)); + self.info + .imported_globals + .push((String::from(module), String::from(field))); + Ok(()) + } + + fn declare_table(&mut self, table: Table) -> WasmResult<()> { + self.info.tables.push(Exportable::new(table)); + Ok(()) + } + + fn declare_table_import( + &mut self, + table: Table, + module: &'data str, + field: &'data str, + ) -> WasmResult<()> { + self.info.tables.push(Exportable::new(table)); + self.info + .imported_tables + .push((String::from(module), String::from(field))); + Ok(()) + } + + fn declare_table_elements( + &mut self, + _table_index: TableIndex, + _base: Option<GlobalIndex>, + _offset: usize, + _elements: Box<[FuncIndex]>, + ) -> WasmResult<()> { + // We do nothing + Ok(()) + } + + fn declare_passive_element( + &mut self, + _elem_index: ElemIndex, + _segments: Box<[FuncIndex]>, + ) -> WasmResult<()> { + Ok(()) + } + + fn declare_passive_data( + &mut self, + _elem_index: DataIndex, + _segments: &'data [u8], + ) -> WasmResult<()> { + Ok(()) + } + + fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> { + self.info.memories.push(Exportable::new(memory)); + Ok(()) + } + + fn declare_memory_import( + &mut self, + memory: Memory, + module: &'data str, + field: &'data str, + ) -> WasmResult<()> { + self.info.memories.push(Exportable::new(memory)); + self.info + .imported_memories + .push((String::from(module), String::from(field))); + Ok(()) + } + + fn declare_data_initialization( + &mut self, + _memory_index: MemoryIndex, + _base: Option<GlobalIndex>, + _offset: usize, + _data: &'data [u8], + ) -> WasmResult<()> { + // We do nothing + Ok(()) + } + + fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()> { + self.info.functions[func_index] + .export_names + .push(String::from(name)); + Ok(()) + } + + fn declare_table_export( + &mut self, + table_index: TableIndex, + name: &'data str, + ) -> WasmResult<()> { + self.info.tables[table_index] + .export_names + .push(String::from(name)); + Ok(()) + } + + fn declare_memory_export( + &mut self, + memory_index: MemoryIndex, + name: &'data str, + ) -> WasmResult<()> { + self.info.memories[memory_index] + .export_names + .push(String::from(name)); + Ok(()) + } + + fn declare_global_export( + &mut self, + global_index: GlobalIndex, + name: &'data str, + ) -> WasmResult<()> { + self.info.globals[global_index] + .export_names + .push(String::from(name)); + Ok(()) + } + + fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> { + debug_assert!(self.info.start_func.is_none()); + self.info.start_func = Some(func_index); + Ok(()) + } + + fn define_function_body( + &mut self, + mut validator: FuncValidator<ValidatorResources>, + body: FunctionBody<'data>, + ) -> WasmResult<()> { + self.func_bytecode_sizes + .push(body.get_binary_reader().bytes_remaining()); + let func = { + let mut func_environ = DummyFuncEnvironment::new(&self.info, self.return_mode); + let func_index = + FuncIndex::new(self.get_num_func_imports() + self.info.function_bodies.len()); + let name = get_func_name(func_index); + let sig = func_environ.vmctx_sig(self.get_func_type(func_index)); + let mut func = ir::Function::with_name_signature(name, sig); + if self.debug_info { + func.collect_debug_info(); + } + self.trans + .translate_body(&mut validator, body, &mut func, &mut func_environ)?; + func + }; + self.info.function_bodies.push(func); + Ok(()) + } + + fn declare_module_name(&mut self, name: &'data str) { + self.module_name = Some(String::from(name)); + } + + fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) { + self.function_names[func_index] = String::from(name); + } + + fn wasm_features(&self) -> WasmFeatures { + WasmFeatures { + multi_value: true, + simd: true, + reference_types: true, + bulk_memory: true, + ..WasmFeatures::default() + } + } +} diff --git a/third_party/rust/cranelift-wasm/src/environ/mod.rs b/third_party/rust/cranelift-wasm/src/environ/mod.rs new file mode 100644 index 0000000000..bb4b7cc34e --- /dev/null +++ b/third_party/rust/cranelift-wasm/src/environ/mod.rs @@ -0,0 +1,11 @@ +//! Support for configurable wasm translation. + +mod dummy; +#[macro_use] +mod spec; + +pub use crate::environ::dummy::DummyEnvironment; +pub use crate::environ::spec::{ + FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment, WasmError, + WasmFuncType, WasmResult, WasmType, +}; diff --git a/third_party/rust/cranelift-wasm/src/environ/spec.rs b/third_party/rust/cranelift-wasm/src/environ/spec.rs new file mode 100644 index 0000000000..149e55d6dc --- /dev/null +++ b/third_party/rust/cranelift-wasm/src/environ/spec.rs @@ -0,0 +1,915 @@ +//! All the runtime support necessary for the wasm to cranelift translation is formalized by the +//! traits `FunctionEnvironment` and `ModuleEnvironment`. +//! +//! There are skeleton implementations of these traits in the `dummy` module, and complete +//! implementations in [Wasmtime]. +//! +//! [Wasmtime]: https://github.com/bytecodealliance/wasmtime + +use crate::state::FuncTranslationState; +use crate::translation_utils::{ + DataIndex, ElemIndex, EntityType, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, Table, + TableIndex, TypeIndex, +}; +use core::convert::From; +use core::convert::TryFrom; +use cranelift_codegen::cursor::FuncCursor; +use cranelift_codegen::ir::immediates::Offset32; +use cranelift_codegen::ir::{self, InstBuilder}; +use cranelift_codegen::isa::TargetFrontendConfig; +use cranelift_frontend::FunctionBuilder; +#[cfg(feature = "enable-serde")] +use serde::{Deserialize, Serialize}; +use std::boxed::Box; +use std::string::ToString; +use thiserror::Error; +use wasmparser::ValidatorResources; +use wasmparser::{BinaryReaderError, FuncValidator, FunctionBody, Operator, WasmFeatures}; + +/// WebAssembly value type -- equivalent of `wasmparser`'s Type. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub enum WasmType { + /// I32 type + I32, + /// I64 type + I64, + /// F32 type + F32, + /// F64 type + F64, + /// V128 type + V128, + /// FuncRef type + FuncRef, + /// ExternRef type + ExternRef, +} + +impl TryFrom<wasmparser::Type> for WasmType { + type Error = WasmError; + fn try_from(ty: wasmparser::Type) -> Result<Self, Self::Error> { + use wasmparser::Type::*; + match ty { + I32 => Ok(WasmType::I32), + I64 => Ok(WasmType::I64), + F32 => Ok(WasmType::F32), + F64 => Ok(WasmType::F64), + V128 => Ok(WasmType::V128), + FuncRef => Ok(WasmType::FuncRef), + ExternRef => Ok(WasmType::ExternRef), + EmptyBlockType | Func => Err(WasmError::InvalidWebAssembly { + message: "unexpected value type".to_string(), + offset: 0, + }), + } + } +} + +impl From<WasmType> for wasmparser::Type { + fn from(ty: WasmType) -> wasmparser::Type { + match ty { + WasmType::I32 => wasmparser::Type::I32, + WasmType::I64 => wasmparser::Type::I64, + WasmType::F32 => wasmparser::Type::F32, + WasmType::F64 => wasmparser::Type::F64, + WasmType::V128 => wasmparser::Type::V128, + WasmType::FuncRef => wasmparser::Type::FuncRef, + WasmType::ExternRef => wasmparser::Type::ExternRef, + } + } +} + +/// WebAssembly function type -- equivalent of `wasmparser`'s FuncType. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub struct WasmFuncType { + /// Function params types. + pub params: Box<[WasmType]>, + /// Returns params types. + pub returns: Box<[WasmType]>, +} + +impl TryFrom<wasmparser::FuncType> for WasmFuncType { + type Error = WasmError; + fn try_from(ty: wasmparser::FuncType) -> Result<Self, Self::Error> { + Ok(Self { + params: ty + .params + .into_vec() + .into_iter() + .map(WasmType::try_from) + .collect::<Result<_, Self::Error>>()?, + returns: ty + .returns + .into_vec() + .into_iter() + .map(WasmType::try_from) + .collect::<Result<_, Self::Error>>()?, + }) + } +} + +/// The value of a WebAssembly global variable. +#[derive(Clone, Copy)] +pub enum GlobalVariable { + /// This is a constant global with a value known at compile time. + Const(ir::Value), + + /// This is a variable in memory that should be referenced through a `GlobalValue`. + Memory { + /// The address of the global variable storage. + gv: ir::GlobalValue, + /// An offset to add to the address. + offset: Offset32, + /// The global variable's type. + ty: ir::Type, + }, + + /// This is a global variable that needs to be handled by the environment. + Custom, +} + +/// A WebAssembly translation error. +/// +/// When a WebAssembly function can't be translated, one of these error codes will be returned +/// to describe the failure. +#[derive(Error, Debug)] +pub enum WasmError { + /// The input WebAssembly code is invalid. + /// + /// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly + /// code. This should never happen for validated WebAssembly code. + #[error("Invalid input WebAssembly code at offset {offset}: {message}")] + InvalidWebAssembly { + /// A string describing the validation error. + message: std::string::String, + /// The bytecode offset where the error occurred. + offset: usize, + }, + + /// A feature used by the WebAssembly code is not supported by the embedding environment. + /// + /// Embedding environments may have their own limitations and feature restrictions. + #[error("Unsupported feature: {0}")] + Unsupported(std::string::String), + + /// An implementation limit was exceeded. + /// + /// Cranelift can compile very large and complicated functions, but the [implementation has + /// limits][limits] that cause compilation to fail when they are exceeded. + /// + /// [limits]: https://github.com/bytecodealliance/wasmtime/blob/main/cranelift/docs/ir.md#implementation-limits + #[error("Implementation limit exceeded")] + ImplLimitExceeded, + + /// Any user-defined error. + #[error("User error: {0}")] + User(std::string::String), +} + +/// Return an `Err(WasmError::Unsupported(msg))` where `msg` the string built by calling `format!` +/// on the arguments to this macro. +#[macro_export] +macro_rules! wasm_unsupported { + ($($arg:tt)*) => { $crate::environ::WasmError::Unsupported(format!($($arg)*)) } +} + +impl From<BinaryReaderError> for WasmError { + /// Convert from a `BinaryReaderError` to a `WasmError`. + fn from(e: BinaryReaderError) -> Self { + Self::InvalidWebAssembly { + message: e.message().into(), + offset: e.offset(), + } + } +} + +/// A convenient alias for a `Result` that uses `WasmError` as the error type. +pub type WasmResult<T> = Result<T, WasmError>; + +/// How to return from functions. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum ReturnMode { + /// Use normal return instructions as needed. + NormalReturns, + /// Use a single fallthrough return at the end of the function. + FallthroughReturn, +} + +/// Environment affecting the translation of a WebAssembly. +pub trait TargetEnvironment { + /// Get the information needed to produce Cranelift IR for the given target. + fn target_config(&self) -> TargetFrontendConfig; + + /// Get the Cranelift integer type to use for native pointers. + /// + /// This returns `I64` for 64-bit architectures and `I32` for 32-bit architectures. + fn pointer_type(&self) -> ir::Type { + ir::Type::int(u16::from(self.target_config().pointer_bits())).unwrap() + } + + /// Get the size of a native pointer, in bytes. + fn pointer_bytes(&self) -> u8 { + self.target_config().pointer_bytes() + } + + /// Get the Cranelift reference type to use for the given Wasm reference + /// type. + /// + /// By default, this returns `R64` for 64-bit architectures and `R32` for + /// 32-bit architectures. If you override this, then you should also + /// override `FuncEnvironment::{translate_ref_null, translate_ref_is_null}` + /// as well. + fn reference_type(&self, ty: WasmType) -> ir::Type { + let _ = ty; + match self.pointer_type() { + ir::types::I32 => ir::types::R32, + ir::types::I64 => ir::types::R64, + _ => panic!("unsupported pointer type"), + } + } +} + +/// Environment affecting the translation of a single WebAssembly function. +/// +/// A `FuncEnvironment` trait object is required to translate a WebAssembly function to Cranelift +/// IR. The function environment provides information about the WebAssembly module as well as the +/// runtime environment. +pub trait FuncEnvironment: TargetEnvironment { + /// Is the given parameter of the given function a wasm-level parameter, as opposed to a hidden + /// parameter added for use by the implementation? + fn is_wasm_parameter(&self, signature: &ir::Signature, index: usize) -> bool { + signature.params[index].purpose == ir::ArgumentPurpose::Normal + } + + /// Is the given return of the given function a wasm-level parameter, as + /// opposed to a hidden parameter added for use by the implementation? + fn is_wasm_return(&self, signature: &ir::Signature, index: usize) -> bool { + signature.returns[index].purpose == ir::ArgumentPurpose::Normal + } + + /// Should the code be structured to use a single `fallthrough_return` instruction at the end + /// of the function body, rather than `return` instructions as needed? This is used by VMs + /// to append custom epilogues. + fn return_mode(&self) -> ReturnMode { + ReturnMode::NormalReturns + } + + /// Set up the necessary preamble definitions in `func` to access the global variable + /// identified by `index`. + /// + /// The index space covers both imported globals and globals defined by the module. + /// + /// Return the global variable reference that should be used to access the global and the + /// WebAssembly type of the global. + fn make_global( + &mut self, + func: &mut ir::Function, + index: GlobalIndex, + ) -> WasmResult<GlobalVariable>; + + /// Set up the necessary preamble definitions in `func` to access the linear memory identified + /// by `index`. + /// + /// The index space covers both imported and locally declared memories. + fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult<ir::Heap>; + + /// Set up the necessary preamble definitions in `func` to access the table identified + /// by `index`. + /// + /// The index space covers both imported and locally declared tables. + fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> WasmResult<ir::Table>; + + /// Set up a signature definition in the preamble of `func` that can be used for an indirect + /// call with signature `index`. + /// + /// The signature may contain additional arguments needed for an indirect call, but the + /// arguments marked as `ArgumentPurpose::Normal` must correspond to the WebAssembly signature + /// arguments. + /// + /// The signature will only be used for indirect calls, even if the module has direct function + /// calls with the same WebAssembly type. + fn make_indirect_sig( + &mut self, + func: &mut ir::Function, + index: TypeIndex, + ) -> WasmResult<ir::SigRef>; + + /// Set up an external function definition in the preamble of `func` that can be used to + /// directly call the function `index`. + /// + /// The index space covers both imported functions and functions defined in the current module. + /// + /// The function's signature may contain additional arguments needed for a direct call, but the + /// arguments marked as `ArgumentPurpose::Normal` must correspond to the WebAssembly signature + /// arguments. + /// + /// The function's signature will only be used for direct calls, even if the module has + /// indirect calls with the same WebAssembly type. + fn make_direct_func( + &mut self, + func: &mut ir::Function, + index: FuncIndex, + ) -> WasmResult<ir::FuncRef>; + + /// Translate a `call_indirect` WebAssembly instruction at `pos`. + /// + /// Insert instructions at `pos` for an indirect call to the function `callee` in the table + /// `table_index` with WebAssembly signature `sig_index`. The `callee` value will have type + /// `i32`. + /// + /// The signature `sig_ref` was previously created by `make_indirect_sig()`. + /// + /// Return the call instruction whose results are the WebAssembly return values. + #[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))] + fn translate_call_indirect( + &mut self, + pos: FuncCursor, + table_index: TableIndex, + table: ir::Table, + sig_index: TypeIndex, + sig_ref: ir::SigRef, + callee: ir::Value, + call_args: &[ir::Value], + ) -> WasmResult<ir::Inst>; + + /// Translate a `call` WebAssembly instruction at `pos`. + /// + /// Insert instructions at `pos` for a direct call to the function `callee_index`. + /// + /// The function reference `callee` was previously created by `make_direct_func()`. + /// + /// Return the call instruction whose results are the WebAssembly return values. + fn translate_call( + &mut self, + mut pos: FuncCursor, + _callee_index: FuncIndex, + callee: ir::FuncRef, + call_args: &[ir::Value], + ) -> WasmResult<ir::Inst> { + Ok(pos.ins().call(callee, call_args)) + } + + /// Translate a `memory.grow` WebAssembly instruction. + /// + /// The `index` provided identifies the linear memory to grow, and `heap` is the heap reference + /// returned by `make_heap` for the same index. + /// + /// The `val` value is the requested memory size in pages. + /// + /// Returns the old size (in pages) of the memory. + fn translate_memory_grow( + &mut self, + pos: FuncCursor, + index: MemoryIndex, + heap: ir::Heap, + val: ir::Value, + ) -> WasmResult<ir::Value>; + + /// Translates a `memory.size` WebAssembly instruction. + /// + /// The `index` provided identifies the linear memory to query, and `heap` is the heap reference + /// returned by `make_heap` for the same index. + /// + /// Returns the size in pages of the memory. + fn translate_memory_size( + &mut self, + pos: FuncCursor, + index: MemoryIndex, + heap: ir::Heap, + ) -> WasmResult<ir::Value>; + + /// Translate a `memory.copy` WebAssembly instruction. + /// + /// The `index` provided identifies the linear memory to query, and `heap` is the heap reference + /// returned by `make_heap` for the same index. + fn translate_memory_copy( + &mut self, + pos: FuncCursor, + src_index: MemoryIndex, + src_heap: ir::Heap, + dst_index: MemoryIndex, + dst_heap: ir::Heap, + dst: ir::Value, + src: ir::Value, + len: ir::Value, + ) -> WasmResult<()>; + + /// Translate a `memory.fill` WebAssembly instruction. + /// + /// The `index` provided identifies the linear memory to query, and `heap` is the heap reference + /// returned by `make_heap` for the same index. + fn translate_memory_fill( + &mut self, + pos: FuncCursor, + index: MemoryIndex, + heap: ir::Heap, + dst: ir::Value, + val: ir::Value, + len: ir::Value, + ) -> WasmResult<()>; + + /// Translate a `memory.init` WebAssembly instruction. + /// + /// The `index` provided identifies the linear memory to query, and `heap` is the heap reference + /// returned by `make_heap` for the same index. `seg_index` is the index of the segment to copy + /// from. + #[allow(clippy::too_many_arguments)] + fn translate_memory_init( + &mut self, + pos: FuncCursor, + index: MemoryIndex, + heap: ir::Heap, + seg_index: u32, + dst: ir::Value, + src: ir::Value, + len: ir::Value, + ) -> WasmResult<()>; + + /// Translate a `data.drop` WebAssembly instruction. + fn translate_data_drop(&mut self, pos: FuncCursor, seg_index: u32) -> WasmResult<()>; + + /// Translate a `table.size` WebAssembly instruction. + fn translate_table_size( + &mut self, + pos: FuncCursor, + index: TableIndex, + table: ir::Table, + ) -> WasmResult<ir::Value>; + + /// Translate a `table.grow` WebAssembly instruction. + fn translate_table_grow( + &mut self, + pos: FuncCursor, + table_index: TableIndex, + table: ir::Table, + delta: ir::Value, + init_value: ir::Value, + ) -> WasmResult<ir::Value>; + + /// Translate a `table.get` WebAssembly instruction. + fn translate_table_get( + &mut self, + builder: &mut FunctionBuilder, + table_index: TableIndex, + table: ir::Table, + index: ir::Value, + ) -> WasmResult<ir::Value>; + + /// Translate a `table.set` WebAssembly instruction. + fn translate_table_set( + &mut self, + builder: &mut FunctionBuilder, + table_index: TableIndex, + table: ir::Table, + value: ir::Value, + index: ir::Value, + ) -> WasmResult<()>; + + /// Translate a `table.copy` WebAssembly instruction. + #[allow(clippy::too_many_arguments)] + fn translate_table_copy( + &mut self, + pos: FuncCursor, + dst_table_index: TableIndex, + dst_table: ir::Table, + src_table_index: TableIndex, + src_table: ir::Table, + dst: ir::Value, + src: ir::Value, + len: ir::Value, + ) -> WasmResult<()>; + + /// Translate a `table.fill` WebAssembly instruction. + fn translate_table_fill( + &mut self, + pos: FuncCursor, + table_index: TableIndex, + dst: ir::Value, + val: ir::Value, + len: ir::Value, + ) -> WasmResult<()>; + + /// Translate a `table.init` WebAssembly instruction. + #[allow(clippy::too_many_arguments)] + fn translate_table_init( + &mut self, + pos: FuncCursor, + seg_index: u32, + table_index: TableIndex, + table: ir::Table, + dst: ir::Value, + src: ir::Value, + len: ir::Value, + ) -> WasmResult<()>; + + /// Translate a `elem.drop` WebAssembly instruction. + fn translate_elem_drop(&mut self, pos: FuncCursor, seg_index: u32) -> WasmResult<()>; + + /// Translate a `ref.null T` WebAssembly instruction. + /// + /// By default, translates into a null reference type. + /// + /// Override this if you don't use Cranelift reference types for all Wasm + /// reference types (e.g. you use a raw pointer for `funcref`s) or if the + /// null sentinel is not a null reference type pointer for your type. If you + /// override this method, then you should also override + /// `translate_ref_is_null` as well. + fn translate_ref_null(&mut self, mut pos: FuncCursor, ty: WasmType) -> WasmResult<ir::Value> { + let _ = ty; + Ok(pos.ins().null(self.reference_type(ty))) + } + + /// Translate a `ref.is_null` WebAssembly instruction. + /// + /// By default, assumes that `value` is a Cranelift reference type, and that + /// a null Cranelift reference type is the null value for all Wasm reference + /// types. + /// + /// If you override this method, you probably also want to override + /// `translate_ref_null` as well. + fn translate_ref_is_null( + &mut self, + mut pos: FuncCursor, + value: ir::Value, + ) -> WasmResult<ir::Value> { + let is_null = pos.ins().is_null(value); + Ok(pos.ins().bint(ir::types::I32, is_null)) + } + + /// Translate a `ref.func` WebAssembly instruction. + fn translate_ref_func( + &mut self, + pos: FuncCursor, + func_index: FuncIndex, + ) -> WasmResult<ir::Value>; + + /// Translate a `global.get` WebAssembly instruction at `pos` for a global + /// that is custom. + fn translate_custom_global_get( + &mut self, + pos: FuncCursor, + global_index: GlobalIndex, + ) -> WasmResult<ir::Value>; + + /// Translate a `global.set` WebAssembly instruction at `pos` for a global + /// that is custom. + fn translate_custom_global_set( + &mut self, + pos: FuncCursor, + global_index: GlobalIndex, + val: ir::Value, + ) -> WasmResult<()>; + + /// Translate an `i32.atomic.wait` or `i64.atomic.wait` WebAssembly instruction. + /// The `index` provided identifies the linear memory containing the value + /// to wait on, and `heap` is the heap reference returned by `make_heap` + /// for the same index. Whether the waited-on value is 32- or 64-bit can be + /// determined by examining the type of `expected`, which must be only I32 or I64. + /// + /// Returns an i32, which is negative if the helper call failed. + fn translate_atomic_wait( + &mut self, + pos: FuncCursor, + index: MemoryIndex, + heap: ir::Heap, + addr: ir::Value, + expected: ir::Value, + timeout: ir::Value, + ) -> WasmResult<ir::Value>; + + /// Translate an `atomic.notify` WebAssembly instruction. + /// The `index` provided identifies the linear memory containing the value + /// to wait on, and `heap` is the heap reference returned by `make_heap` + /// for the same index. + /// + /// Returns an i64, which is negative if the helper call failed. + fn translate_atomic_notify( + &mut self, + pos: FuncCursor, + index: MemoryIndex, + heap: ir::Heap, + addr: ir::Value, + count: ir::Value, + ) -> WasmResult<ir::Value>; + + /// Emit code at the beginning of every wasm loop. + /// + /// This can be used to insert explicit interrupt or safepoint checking at + /// the beginnings of loops. + fn translate_loop_header(&mut self, _pos: FuncCursor) -> WasmResult<()> { + // By default, don't emit anything. + Ok(()) + } + + /// Optional callback for the `FunctionEnvironment` performing this translation to maintain + /// internal state or prepare custom state for the operator to translate + fn before_translate_operator( + &mut self, + _op: &Operator, + _builder: &mut FunctionBuilder, + _state: &FuncTranslationState, + ) -> WasmResult<()> { + Ok(()) + } + + /// Optional callback for the `FunctionEnvironment` performing this translation to maintain + /// internal state or finalize custom state for the operator that was translated + fn after_translate_operator( + &mut self, + _op: &Operator, + _builder: &mut FunctionBuilder, + _state: &FuncTranslationState, + ) -> WasmResult<()> { + Ok(()) + } +} + +/// An object satisfying the `ModuleEnvironment` trait can be passed as argument to the +/// [`translate_module`](fn.translate_module.html) function. These methods should not be called +/// by the user, they are only for `cranelift-wasm` internal use. +pub trait ModuleEnvironment<'data>: TargetEnvironment { + /// Provides the number of types up front. By default this does nothing, but + /// implementations can use this to preallocate memory if desired. + fn reserve_types(&mut self, _num: u32) -> WasmResult<()> { + Ok(()) + } + + /// Declares a function signature to the environment. + fn declare_type_func( + &mut self, + wasm_func_type: WasmFuncType, + sig: ir::Signature, + ) -> WasmResult<()>; + + /// Declares a module type signature to the environment. + fn declare_type_module( + &mut self, + imports: &[(&'data str, Option<&'data str>, EntityType)], + exports: &[(&'data str, EntityType)], + ) -> WasmResult<()> { + drop((imports, exports)); + Err(WasmError::Unsupported("module linking".to_string())) + } + + /// Declares an instance type signature to the environment. + fn declare_type_instance(&mut self, exports: &[(&'data str, EntityType)]) -> WasmResult<()> { + drop(exports); + Err(WasmError::Unsupported("module linking".to_string())) + } + + /// Provides the number of imports up front. By default this does nothing, but + /// implementations can use this to preallocate memory if desired. + fn reserve_imports(&mut self, _num: u32) -> WasmResult<()> { + Ok(()) + } + + /// Declares a function import to the environment. + fn declare_func_import( + &mut self, + index: TypeIndex, + module: &'data str, + field: &'data str, + ) -> WasmResult<()>; + + /// Declares a table import to the environment. + fn declare_table_import( + &mut self, + table: Table, + module: &'data str, + field: &'data str, + ) -> WasmResult<()>; + + /// Declares a memory import to the environment. + fn declare_memory_import( + &mut self, + memory: Memory, + module: &'data str, + field: &'data str, + ) -> WasmResult<()>; + + /// Declares a global import to the environment. + fn declare_global_import( + &mut self, + global: Global, + module: &'data str, + field: &'data str, + ) -> WasmResult<()>; + + /// Declares a module import to the environment. + fn declare_module_import( + &mut self, + ty_index: TypeIndex, + module: &'data str, + field: &'data str, + ) -> WasmResult<()> { + drop((ty_index, module, field)); + Err(WasmError::Unsupported("module linking".to_string())) + } + + /// Declares an instance import to the environment. + fn declare_instance_import( + &mut self, + ty_index: TypeIndex, + module: &'data str, + field: &'data str, + ) -> WasmResult<()> { + drop((ty_index, module, field)); + Err(WasmError::Unsupported("module linking".to_string())) + } + + /// Notifies the implementation that all imports have been declared. + fn finish_imports(&mut self) -> WasmResult<()> { + Ok(()) + } + + /// Provides the number of defined functions up front. By default this does nothing, but + /// implementations can use this to preallocate memory if desired. + fn reserve_func_types(&mut self, _num: u32) -> WasmResult<()> { + Ok(()) + } + + /// Declares the type (signature) of a local function in the module. + fn declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()>; + + /// Provides the number of defined tables up front. By default this does nothing, but + /// implementations can use this to preallocate memory if desired. + fn reserve_tables(&mut self, _num: u32) -> WasmResult<()> { + Ok(()) + } + + /// Declares a table to the environment. + fn declare_table(&mut self, table: Table) -> WasmResult<()>; + + /// Provides the number of defined memories up front. By default this does nothing, but + /// implementations can use this to preallocate memory if desired. + fn reserve_memories(&mut self, _num: u32) -> WasmResult<()> { + Ok(()) + } + + /// Declares a memory to the environment + fn declare_memory(&mut self, memory: Memory) -> WasmResult<()>; + + /// Provides the number of defined globals up front. By default this does nothing, but + /// implementations can use this to preallocate memory if desired. + fn reserve_globals(&mut self, _num: u32) -> WasmResult<()> { + Ok(()) + } + + /// Declares a global to the environment. + fn declare_global(&mut self, global: Global) -> WasmResult<()>; + + /// Provides the number of exports up front. By default this does nothing, but + /// implementations can use this to preallocate memory if desired. + fn reserve_exports(&mut self, _num: u32) -> WasmResult<()> { + Ok(()) + } + + /// Declares a function export to the environment. + fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()>; + + /// Declares a table export to the environment. + fn declare_table_export(&mut self, table_index: TableIndex, name: &'data str) + -> WasmResult<()>; + + /// Declares a memory export to the environment. + fn declare_memory_export( + &mut self, + memory_index: MemoryIndex, + name: &'data str, + ) -> WasmResult<()>; + + /// Declares a global export to the environment. + fn declare_global_export( + &mut self, + global_index: GlobalIndex, + name: &'data str, + ) -> WasmResult<()>; + + /// Notifies the implementation that all exports have been declared. + fn finish_exports(&mut self) -> WasmResult<()> { + Ok(()) + } + + /// Declares the optional start function. + fn declare_start_func(&mut self, index: FuncIndex) -> WasmResult<()>; + + /// Provides the number of element initializers up front. By default this does nothing, but + /// implementations can use this to preallocate memory if desired. + fn reserve_table_elements(&mut self, _num: u32) -> WasmResult<()> { + Ok(()) + } + + /// Fills a declared table with references to functions in the module. + fn declare_table_elements( + &mut self, + table_index: TableIndex, + base: Option<GlobalIndex>, + offset: usize, + elements: Box<[FuncIndex]>, + ) -> WasmResult<()>; + + /// Declare a passive element segment. + fn declare_passive_element( + &mut self, + index: ElemIndex, + elements: Box<[FuncIndex]>, + ) -> WasmResult<()>; + + /// Provides the number of passive data segments up front. + /// + /// By default this does nothing, but implementations may use this to + /// pre-allocate memory if desired. + fn reserve_passive_data(&mut self, count: u32) -> WasmResult<()> { + let _ = count; + Ok(()) + } + + /// Declare a passive data segment. + fn declare_passive_data(&mut self, data_index: DataIndex, data: &'data [u8]) -> WasmResult<()>; + + /// Indicates how many functions the code section reports and the byte + /// offset of where the code sections starts. + fn reserve_function_bodies(&mut self, bodies: u32, code_section_offset: u64) { + drop((bodies, code_section_offset)); + } + + /// Provides the contents of a function body. + fn define_function_body( + &mut self, + validator: FuncValidator<ValidatorResources>, + body: FunctionBody<'data>, + ) -> WasmResult<()>; + + /// Provides the number of data initializers up front. By default this does nothing, but + /// implementations can use this to preallocate memory if desired. + fn reserve_data_initializers(&mut self, _num: u32) -> WasmResult<()> { + Ok(()) + } + + /// Fills a declared memory with bytes at module instantiation. + fn declare_data_initialization( + &mut self, + memory_index: MemoryIndex, + base: Option<GlobalIndex>, + offset: usize, + data: &'data [u8], + ) -> WasmResult<()>; + + /// Declares the name of a module to the environment. + /// + /// By default this does nothing, but implementations can use this to read + /// the module name subsection of the custom name section if desired. + fn declare_module_name(&mut self, _name: &'data str) {} + + /// Declares the name of a function to the environment. + /// + /// By default this does nothing, but implementations can use this to read + /// the function name subsection of the custom name section if desired. + fn declare_func_name(&mut self, _func_index: FuncIndex, _name: &'data str) {} + + /// Declares the name of a function's local to the environment. + /// + /// By default this does nothing, but implementations can use this to read + /// the local name subsection of the custom name section if desired. + fn declare_local_name(&mut self, _func_index: FuncIndex, _local_index: u32, _name: &'data str) { + } + + /// Indicates that a custom section has been found in the wasm file + fn custom_section(&mut self, _name: &'data str, _data: &'data [u8]) -> WasmResult<()> { + Ok(()) + } + + /// Returns the list of enabled wasm features this translation will be using. + fn wasm_features(&self) -> WasmFeatures { + WasmFeatures::default() + } + + /// Indicates that this module will have `amount` submodules. + /// + /// Note that this is just child modules of this module, and each child + /// module may have yet more submodules. + fn reserve_modules(&mut self, amount: u32) { + drop(amount); + } + + /// Called at the beginning of translating a module. + /// + /// The `index` argument is a monotonically increasing index which + /// corresponds to the nth module that's being translated. This is not the + /// 32-bit index in the current module's index space. For example the first + /// call to `module_start` will have index 0. + /// + /// Note that for nested modules this may be called multiple times. + fn module_start(&mut self, index: usize) { + drop(index); + } + + /// Called at the end of translating a module. + /// + /// Note that for nested modules this may be called multiple times. + fn module_end(&mut self, index: usize) { + drop(index); + } +} |