diff --git a/cranelift/wasm/src/translation_utils.rs b/cranelift/wasm/src/translation_utils.rs index 0a1d43c9eb..f29eb2f18c 100644 --- a/cranelift/wasm/src/translation_utils.rs +++ b/cranelift/wasm/src/translation_utils.rs @@ -1,7 +1,7 @@ //! Helper functions and structures for the translation. use crate::environ::{TargetEnvironment, WasmResult, WasmType}; -use crate::wasm_unsupported; -use core::convert::TryInto; +use crate::{wasm_unsupported, WasmError}; +use core::convert::{TryFrom, TryInto}; use core::u32; use cranelift_codegen::entity::entity_impl; use cranelift_codegen::ir; @@ -195,6 +195,17 @@ pub enum GlobalInit { Import, } +impl Global { + /// Creates a new `Global` type from wasmparser's representation. + pub fn new(ty: wasmparser::GlobalType, initializer: GlobalInit) -> WasmResult { + Ok(Global { + wasm_ty: ty.content_type.try_into()?, + mutability: ty.mutable, + initializer, + }) + } +} + /// WebAssembly table. #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -207,6 +218,18 @@ pub struct Table { pub maximum: Option, } +impl TryFrom for Table { + type Error = WasmError; + + fn try_from(ty: wasmparser::TableType) -> WasmResult { + Ok(Table { + wasm_ty: ty.element_type.try_into()?, + minimum: ty.initial, + maximum: ty.maximum, + }) + } +} + /// WebAssembly table element. Can be a function or a scalar type. #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -231,6 +254,17 @@ pub struct Memory { pub memory64: bool, } +impl From for Memory { + fn from(ty: wasmparser::MemoryType) -> Memory { + Memory { + minimum: ty.initial, + maximum: ty.maximum, + shared: ty.shared, + memory64: ty.memory64, + } + } +} + /// WebAssembly event. #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -239,6 +273,14 @@ pub struct Tag { pub ty: TypeIndex, } +impl From for Tag { + fn from(ty: wasmparser::TagType) -> Tag { + Tag { + ty: TypeIndex::from_u32(ty.type_index), + } + } +} + /// Helper function translating wasmparser types to Cranelift types when possible. pub fn type_to_type( ty: wasmparser::Type, diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs index 320d647f29..85d1c056fa 100644 --- a/crates/environ/src/module_environ.rs +++ b/crates/environ/src/module_environ.rs @@ -3,21 +3,25 @@ use crate::module::{ ModuleSignature, ModuleType, ModuleUpvar, TableInitializer, TablePlan, TypeTables, }; use crate::tunables::Tunables; +use cranelift_codegen::ir::immediates::V128Imm; use cranelift_codegen::packed_option::ReservedValue; use cranelift_entity::PrimaryMap; use cranelift_wasm::{ - self, translate_module, Alias, DataIndex, DefinedFuncIndex, ElemIndex, EntityIndex, EntityType, - FuncIndex, Global, GlobalIndex, GlobalInit, InstanceIndex, InstanceTypeIndex, Memory, - MemoryIndex, ModuleIndex, ModuleTypeIndex, SignatureIndex, Table, TableIndex, TypeIndex, - WasmError, WasmFuncType, WasmResult, + self, DataIndex, DefinedFuncIndex, ElemIndex, EntityIndex, EntityType, FuncIndex, Global, + GlobalIndex, GlobalInit, InstanceIndex, InstanceTypeIndex, MemoryIndex, ModuleIndex, + ModuleTypeIndex, SignatureIndex, TableIndex, TypeIndex, WasmError, WasmFuncType, WasmResult, }; use std::collections::{hash_map::Entry, HashMap}; -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::mem; use std::path::PathBuf; use std::sync::Arc; use wasmparser::Type as WasmType; -use wasmparser::{FuncValidator, FunctionBody, ValidatorResources, WasmFeatures}; +use wasmparser::{ + Alias, DataKind, ElementItem, ElementKind, ExternalKind, FuncValidator, FunctionBody, + ImportSectionEntryType, NameSectionReader, Naming, Operator, Parser, Payload, TypeDef, + Validator, ValidatorResources, WasmFeatures, +}; /// Object containing the standalone environment information. pub struct ModuleEnvironment<'data> { @@ -165,16 +169,713 @@ impl<'data> ModuleEnvironment<'data> { mut self, data: &'data [u8], ) -> WasmResult<(usize, Vec>, TypeTables)> { - translate_module(data, &mut self)?; + let mut validator = Validator::new(); + validator.wasm_features(self.features); + + for payload in Parser::new(0).parse_all(data) { + self.translate_payload(&mut validator, payload?)?; + } + assert!(self.results.len() > 0); Ok((self.results.len() - 1, self.results, self.types)) } - fn declare_export(&mut self, export: EntityIndex, name: &str) -> WasmResult<()> { - self.result - .module - .exports - .insert(String::from(name), export); + fn translate_payload( + &mut self, + validator: &mut Validator, + payload: Payload<'data>, + ) -> WasmResult<()> { + match payload { + Payload::Version { num, range } => { + validator.version(num, &range)?; + + // If this is the first time this method is called, nothing to + // do. + if self.first_module { + self.first_module = false; + } else { + // Reset our internal state for a new module by saving the + // current module in `results`. + let in_progress = mem::replace(&mut self.result, ModuleTranslation::default()); + self.in_progress.push(in_progress); + self.modules_to_be -= 1; + } + } + + Payload::End => { + validator.end()?; + + self.result.creation_artifacts.shrink_to_fit(); + self.result.creation_modules.shrink_to_fit(); + + let (record_initializer, mut done) = match self.in_progress.pop() { + Some(m) => (true, mem::replace(&mut self.result, m)), + None => (false, mem::take(&mut self.result)), + }; + + if record_initializer { + // Record the type of the module we just finished in our own + // module's list of modules. + let sig = self.gen_type_of_module(&done.module); + self.result.module.modules.push(sig); + + // The root module will store the artifacts for this + // finished module at `artifact_index`. This then needs to + // be inherited by all later modules coming down to our + // now-current `self.result`... + let mut artifact_index = self.results.len(); + for result in self.in_progress.iter_mut().chain(Some(&mut self.result)) { + result.creation_artifacts.push(artifact_index); + artifact_index = result.creation_artifacts.len() - 1; + } + // ... and then `self.result` needs to create a new module + // with whatever was record to save off as its own + // artifacts/modules. + self.result + .module + .initializers + .push(Initializer::CreateModule { + artifact_index, + artifacts: mem::take(&mut done.creation_artifacts), + modules: mem::take(&mut done.creation_modules), + }); + } + + // And the final step is to insert the module into the list of + // finished modules to get returned at the end. + self.results.push(done); + } + + Payload::TypeSection(types) => { + validator.type_section(&types)?; + let num = usize::try_from(types.get_count()).unwrap(); + self.result.module.types.reserve(num); + self.types.wasm_signatures.reserve(num); + + for ty in types { + match ty? { + TypeDef::Func(wasm_func_ty) => { + self.declare_type_func(wasm_func_ty.try_into()?)?; + } + TypeDef::Module(t) => { + let imports = t + .imports + .iter() + .map(|i| Ok((i.module, i.field, self.entity_type(i.ty)?))) + .collect::>>()?; + let exports = t + .exports + .iter() + .map(|e| Ok((e.name, self.entity_type(e.ty)?))) + .collect::>>()?; + self.declare_type_module(&imports, &exports)?; + } + TypeDef::Instance(t) => { + let exports = t + .exports + .iter() + .map(|e| Ok((e.name, self.entity_type(e.ty)?))) + .collect::>>()?; + self.declare_type_instance(&exports)?; + } + } + } + } + + Payload::ImportSection(imports) => { + validator.import_section(&imports)?; + + let cnt = usize::try_from(imports.get_count()).unwrap(); + self.result.module.initializers.reserve(cnt); + + for entry in imports { + let import = entry?; + let ty = match import.ty { + ImportSectionEntryType::Function(index) => { + let index = TypeIndex::from_u32(index); + let sig_index = self.result.module.types[index].unwrap_function(); + self.result.module.num_imported_funcs += 1; + self.result.debuginfo.wasm_file.imported_func_count += 1; + EntityType::Function(sig_index) + } + ImportSectionEntryType::Module(index) => { + let index = TypeIndex::from_u32(index); + let signature = self.type_to_module_type(index)?; + EntityType::Module(signature) + } + ImportSectionEntryType::Instance(index) => { + let index = TypeIndex::from_u32(index); + let signature = self.type_to_instance_type(index)?; + EntityType::Instance(signature) + } + ImportSectionEntryType::Memory(ty) => { + if ty.shared { + return Err(WasmError::Unsupported("shared memories".to_owned())); + } + self.result.module.num_imported_memories += 1; + EntityType::Memory(ty.into()) + } + ImportSectionEntryType::Global(ty) => { + self.result.module.num_imported_globals += 1; + EntityType::Global(Global::new(ty, GlobalInit::Import)?) + } + ImportSectionEntryType::Table(ty) => { + self.result.module.num_imported_tables += 1; + EntityType::Table(ty.try_into()?) + } + + // doesn't get past validation + ImportSectionEntryType::Tag(_) => unreachable!(), + }; + self.declare_import(import.module, import.field, ty); + } + } + + Payload::FunctionSection(functions) => { + validator.function_section(&functions)?; + + let cnt = usize::try_from(functions.get_count()).unwrap(); + self.result.module.functions.reserve_exact(cnt); + + for entry in functions { + let sigindex = entry?; + let ty = TypeIndex::from_u32(sigindex); + let sig_index = self.result.module.types[ty].unwrap_function(); + self.result.module.functions.push(sig_index); + } + } + + Payload::TableSection(tables) => { + validator.table_section(&tables)?; + let cnt = usize::try_from(tables.get_count()).unwrap(); + self.result.module.table_plans.reserve_exact(cnt); + + for entry in tables { + let table = entry?.try_into()?; + let plan = TablePlan::for_table(table, &self.tunables); + self.result.module.table_plans.push(plan); + } + } + + Payload::MemorySection(memories) => { + validator.memory_section(&memories)?; + + let cnt = usize::try_from(memories.get_count()).unwrap(); + self.result.module.memory_plans.reserve_exact(cnt); + + for entry in memories { + let memory = entry?; + if memory.shared { + return Err(WasmError::Unsupported("shared memories".to_owned())); + } + let plan = MemoryPlan::for_memory(memory.into(), &self.tunables); + self.result.module.memory_plans.push(plan); + } + } + + Payload::TagSection(tags) => { + validator.tag_section(&tags)?; + + // This feature isn't enabled at this time, so we should + // never get here. + unreachable!(); + } + + Payload::GlobalSection(globals) => { + validator.global_section(&globals)?; + + let cnt = usize::try_from(globals.get_count()).unwrap(); + self.result.module.globals.reserve_exact(cnt); + + for entry in globals { + let wasmparser::Global { ty, init_expr } = entry?; + let mut init_expr_reader = init_expr.get_binary_reader(); + let initializer = match init_expr_reader.read_operator()? { + Operator::I32Const { value } => GlobalInit::I32Const(value), + Operator::I64Const { value } => GlobalInit::I64Const(value), + Operator::F32Const { value } => GlobalInit::F32Const(value.bits()), + Operator::F64Const { value } => GlobalInit::F64Const(value.bits()), + Operator::V128Const { value } => { + GlobalInit::V128Const(V128Imm::from(value.bytes().to_vec().as_slice())) + } + Operator::RefNull { ty: _ } => GlobalInit::RefNullConst, + Operator::RefFunc { function_index } => { + let index = FuncIndex::from_u32(function_index); + self.flag_func_possibly_exported(index); + GlobalInit::RefFunc(index) + } + Operator::GlobalGet { global_index } => { + GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index)) + } + s => { + return Err(WasmError::Unsupported(format!( + "unsupported init expr in global section: {:?}", + s + ))); + } + }; + let ty = Global::new(ty, initializer)?; + self.result.module.globals.push(ty); + } + } + + Payload::ExportSection(exports) => { + validator.export_section(&exports)?; + + let cnt = usize::try_from(exports.get_count()).unwrap(); + self.result.module.exports.reserve(cnt); + + for entry in exports { + let wasmparser::Export { field, kind, index } = entry?; + let entity = match kind { + ExternalKind::Function => { + let index = FuncIndex::from_u32(index); + self.flag_func_possibly_exported(index); + EntityIndex::Function(index) + } + ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(index)), + ExternalKind::Memory => EntityIndex::Memory(MemoryIndex::from_u32(index)), + ExternalKind::Global => EntityIndex::Global(GlobalIndex::from_u32(index)), + ExternalKind::Module => EntityIndex::Module(ModuleIndex::from_u32(index)), + ExternalKind::Instance => { + EntityIndex::Instance(InstanceIndex::from_u32(index)) + } + + // this never gets past validation + ExternalKind::Tag | ExternalKind::Type => unreachable!(), + }; + self.result + .module + .exports + .insert(String::from(field), entity); + } + } + + Payload::StartSection { func, range } => { + validator.start_section(func, &range)?; + + let func_index = FuncIndex::from_u32(func); + self.flag_func_possibly_exported(func_index); + debug_assert!(self.result.module.start_func.is_none()); + self.result.module.start_func = Some(func_index); + } + + Payload::ElementSection(elements) => { + validator.element_section(&elements)?; + + let cnt = usize::try_from(elements.get_count()).unwrap(); + self.result.module.table_initializers.reserve_exact(cnt); + + for (index, entry) in elements.into_iter().enumerate() { + let wasmparser::Element { kind, items, ty: _ } = entry?; + + // Build up a list of `FuncIndex` corresponding to all the + // entries listed in this segment. Note that it's not + // possible to create anything other than a `ref.null + // extern` for externref segments, so those just get + // translate to the reserved value of `FuncIndex`. + let items_reader = items.get_items_reader()?; + let mut elements = + Vec::with_capacity(usize::try_from(items_reader.get_count()).unwrap()); + for item in items_reader { + elements.push(match item? { + ElementItem::Func(f) => { + let f = FuncIndex::from_u32(f); + self.flag_func_possibly_exported(f); + f + } + ElementItem::Null(_ty) => FuncIndex::reserved_value(), + }); + } + + match kind { + ElementKind::Active { + table_index, + init_expr, + } => { + let table_index = TableIndex::from_u32(table_index); + let mut init_expr_reader = init_expr.get_binary_reader(); + let (base, offset) = match init_expr_reader.read_operator()? { + Operator::I32Const { value } => (None, value as u32), + Operator::GlobalGet { global_index } => { + (Some(GlobalIndex::from_u32(global_index)), 0) + } + ref s => { + return Err(WasmError::Unsupported(format!( + "unsupported init expr in element section: {:?}", + s + ))); + } + }; + self.result + .module + .table_initializers + .push(TableInitializer { + table_index, + base, + offset, + elements: elements.into(), + }); + } + + ElementKind::Passive => { + let elem_index = ElemIndex::from_u32(index as u32); + let index = self.result.module.passive_elements.len(); + self.result.module.passive_elements.push(elements.into()); + self.result + .module + .passive_elements_map + .insert(elem_index, index); + } + + ElementKind::Declared => {} + } + } + } + + Payload::CodeSectionStart { count, range, .. } => { + validator.code_section_start(count, &range)?; + let cnt = usize::try_from(count).unwrap(); + self.result.function_body_inputs.reserve_exact(cnt); + self.result.debuginfo.wasm_file.code_section_offset = range.start as u64; + } + + Payload::CodeSectionEntry(mut body) => { + let validator = validator.code_section_entry()?; + let func_index = + self.result.code_index + self.result.module.num_imported_funcs as u32; + let func_index = FuncIndex::from_u32(func_index); + + if self.tunables.generate_native_debuginfo { + let sig_index = self.result.module.functions[func_index]; + let sig = &self.types.wasm_signatures[sig_index]; + let mut locals = Vec::new(); + for pair in body.get_locals_reader()? { + locals.push(pair?); + } + self.result + .debuginfo + .wasm_file + .funcs + .push(FunctionMetadata { + locals: locals.into_boxed_slice(), + params: sig.params.iter().cloned().map(|i| i.into()).collect(), + }); + } + body.allow_memarg64(self.features.memory64); + self.result + .function_body_inputs + .push(FunctionBodyData { validator, body }); + self.result.code_index += 1; + } + + Payload::DataSection(data) => { + validator.data_section(&data)?; + + let initializers = match &mut self.result.module.memory_initialization { + MemoryInitialization::Segmented(i) => i, + _ => unreachable!(), + }; + + let cnt = usize::try_from(data.get_count()).unwrap(); + initializers.reserve_exact(cnt); + + for (index, entry) in data.into_iter().enumerate() { + let wasmparser::Data { kind, data } = entry?; + match kind { + DataKind::Active { + memory_index, + init_expr, + } => { + let memory_index = MemoryIndex::from_u32(memory_index); + let mut init_expr_reader = init_expr.get_binary_reader(); + let (base, offset) = match init_expr_reader.read_operator()? { + Operator::I32Const { value } => (None, value as u64), + Operator::I64Const { value } => (None, value as u64), + Operator::GlobalGet { global_index } => { + (Some(GlobalIndex::from_u32(global_index)), 0) + } + s => { + return Err(WasmError::Unsupported(format!( + "unsupported init expr in data section: {:?}", + s + ))); + } + }; + initializers.push(MemoryInitializer { + memory_index, + base, + offset, + data: data.into(), + }); + } + DataKind::Passive => { + let data_index = DataIndex::from_u32(index as u32); + let index = self.result.module.passive_data.len(); + self.result.module.passive_data.push(Arc::from(data)); + self.result + .module + .passive_data_map + .insert(data_index, index); + } + } + } + } + + Payload::DataCountSection { count, range } => { + validator.data_count_section(count, &range)?; + + // Note: the count passed in here is the *total* segment count + // There is no way to reserve for just the passive segments as + // they are discovered when iterating the data section entries + // Given that the total segment count might be much larger than + // the passive count, do not reserve anything here. + } + + Payload::InstanceSection(s) => { + validator.instance_section(&s)?; + + let cnt = usize::try_from(s.get_count()).unwrap(); + self.result.module.instances.reserve(cnt); + self.result.module.initializers.reserve(cnt); + + for instance in s { + let instance = instance?; + let module = ModuleIndex::from_u32(instance.module()); + let args = instance + .args()? + .into_iter() + .map(|arg| { + let arg = arg?; + let index = match arg.kind { + ExternalKind::Function => { + EntityIndex::Function(FuncIndex::from_u32(arg.index)) + } + ExternalKind::Table => { + EntityIndex::Table(TableIndex::from_u32(arg.index)) + } + ExternalKind::Memory => { + EntityIndex::Memory(MemoryIndex::from_u32(arg.index)) + } + ExternalKind::Global => { + EntityIndex::Global(GlobalIndex::from_u32(arg.index)) + } + ExternalKind::Module => { + EntityIndex::Module(ModuleIndex::from_u32(arg.index)) + } + ExternalKind::Instance => { + EntityIndex::Instance(InstanceIndex::from_u32(arg.index)) + } + + // this won't pass validation + ExternalKind::Tag | ExternalKind::Type => unreachable!(), + }; + Ok((arg.name.to_string(), index)) + }) + .collect::>()?; + + // Record the type of this instance with the type signature of the + // module we're instantiating and then also add an initializer which + // records that we'll be adding to the instance index space here. + let module_ty = self.result.module.modules[module]; + let instance_ty = self.types.module_signatures[module_ty].exports; + self.result.module.instances.push(instance_ty); + self.result + .module + .initializers + .push(Initializer::Instantiate { module, args }); + } + } + Payload::AliasSection(s) => { + validator.alias_section(&s)?; + + for alias in s { + match alias? { + // Types are easy, we statically know everything so + // we're just copying some pointers from our parent + // module to our own module. + // + // Note that we don't add an initializer for this alias + // because we statically know where all types point to. + Alias::OuterType { + relative_depth, + index, + } => { + let index = TypeIndex::from_u32(index); + let module_idx = self.in_progress.len() - 1 - (relative_depth as usize); + let ty = self.in_progress[module_idx].module.types[index]; + self.result.module.types.push(ty); + } + + // Modules are a bit trickier since we need to record + // how to track the state from the original module down + // to our own. + Alias::OuterModule { + relative_depth, + index, + } => { + let index = ModuleIndex::from_u32(index); + + // First we can copy the type from the parent module + // into our own module to record what type our + // module definition will have. + let module_idx = self.in_progress.len() - 1 - (relative_depth as usize); + let module_ty = self.in_progress[module_idx].module.modules[index]; + self.result.module.modules.push(module_ty); + + // Next we'll be injecting a module value that is + // closed over, and that will be used to define the + // module into the index space. Record an + // initializer about where our module is sourced + // from (which will be stored within each module + // value itself). + let module_index = self.result.creation_modules.len(); + self.result + .module + .initializers + .push(Initializer::DefineModule(module_index)); + + // And finally we need to record a breadcrumb trail + // of how to get the module value into + // `module_index`. The module just after our + // destination module will use a `ModuleIndex` to + // fetch the module value, and everything else + // inbetween will inherit that module's closed-over + // value. + let mut upvar = ModuleUpvar::Local(index); + for outer in self.in_progress[module_idx + 1..].iter_mut() { + let upvar = mem::replace( + &mut upvar, + ModuleUpvar::Inherit(outer.creation_modules.len()), + ); + outer.creation_modules.push(upvar); + } + self.result.creation_modules.push(upvar); + } + + // This case is slightly more involved, we'll be + // recording all the type information for each kind of + // entity, and then we also need to record an + // initialization step to get the export from the + // instance. + Alias::InstanceExport { + instance, + export, + kind: _, + } => { + let instance = InstanceIndex::from_u32(instance); + let ty = self.result.module.instances[instance]; + match &self.types.instance_signatures[ty].exports[export] { + EntityType::Global(g) => { + self.result.module.globals.push(g.clone()); + self.result.module.num_imported_globals += 1; + } + EntityType::Memory(mem) => { + let plan = MemoryPlan::for_memory(*mem, &self.tunables); + self.result.module.memory_plans.push(plan); + self.result.module.num_imported_memories += 1; + } + EntityType::Table(t) => { + let plan = TablePlan::for_table(*t, &self.tunables); + self.result.module.table_plans.push(plan); + self.result.module.num_imported_tables += 1; + } + EntityType::Function(sig) => { + self.result.module.functions.push(*sig); + self.result.module.num_imported_funcs += 1; + self.result.debuginfo.wasm_file.imported_func_count += 1; + } + EntityType::Instance(sig) => { + self.result.module.instances.push(*sig); + } + EntityType::Module(sig) => { + self.result.module.modules.push(*sig); + } + EntityType::Tag(_) => unimplemented!(), + } + self.result + .module + .initializers + .push(Initializer::AliasInstanceExport { + instance, + export: export.to_string(), + }) + } + } + } + } + + Payload::ModuleSectionStart { + count, + range, + size: _, + } => { + validator.module_section_start(count, &range)?; + + // Go ahead and reserve space in the final `results` array for `amount` + // more modules. + self.modules_to_be += count as usize; + self.results.reserve(self.modules_to_be); + + // Then also reserve space in our own local module's metadata fields + // we'll be adding to. + self.result.module.modules.reserve(count as usize); + self.result.module.initializers.reserve(count as usize); + } + + Payload::ModuleSectionEntry { .. } => { + validator.module_section_entry(); + // note that nothing else happens here since we rely on the next + // `Version` payload to recurse in the parsed modules. + } + + Payload::CustomSection { + name: "name", + data, + data_offset, + range: _, + } => { + let result = NameSectionReader::new(data, data_offset) + .map_err(|e| e.into()) + .and_then(|s| self.name_section(s)); + if let Err(e) = result { + log::warn!("failed to parse name section {:?}", e); + } + } + + Payload::CustomSection { + name: "webidl-bindings", + .. + } + | Payload::CustomSection { + name: "wasm-interface-types", + .. + } => { + return Err(WasmError::Unsupported( + "\ +Support for interface types has temporarily been removed from `wasmtime`. + +For more information about this temoprary you can read on the issue online: + + https://github.com/bytecodealliance/wasmtime/issues/1271 + +and for re-adding support for interface types you can see this issue: + + https://github.com/bytecodealliance/wasmtime/issues/677 +" + .to_string(), + )) + } + + Payload::CustomSection { name, data, .. } => { + self.register_dwarf_section(name, data); + } + + Payload::UnknownSection { id, range, .. } => { + validator.unknown_section(id, &range)?; + unreachable!(); + } + } Ok(()) } @@ -309,6 +1010,26 @@ impl<'data> ModuleEnvironment<'data> { } } + fn entity_type(&self, ty: ImportSectionEntryType) -> WasmResult { + Ok(match ty { + ImportSectionEntryType::Function(sig) => { + EntityType::Function(self.type_to_signature(TypeIndex::from_u32(sig))?) + } + ImportSectionEntryType::Module(sig) => { + EntityType::Module(self.type_to_module_type(TypeIndex::from_u32(sig))?) + } + ImportSectionEntryType::Instance(sig) => { + EntityType::Instance(self.type_to_instance_type(TypeIndex::from_u32(sig))?) + } + ImportSectionEntryType::Memory(ty) => EntityType::Memory(ty.into()), + ImportSectionEntryType::Tag(t) => EntityType::Tag(t.into()), + ImportSectionEntryType::Global(ty) => { + EntityType::Global(Global::new(ty, GlobalInit::Import)?) + } + ImportSectionEntryType::Table(ty) => EntityType::Table(ty.try_into()?), + }) + } + fn push_type(&mut self, ty: EntityType) -> EntityIndex { match ty { EntityType::Function(ty) => { @@ -357,24 +1078,10 @@ impl<'data> ModuleEnvironment<'data> { } fn flag_func_possibly_exported(&mut self, func: FuncIndex) { - if func.is_reserved_value() { - return; - } if let Some(idx) = self.result.module.defined_func_index(func) { self.result.module.possibly_exported_funcs.insert(idx); } } -} - -/// This trait is useful for `translate_module` because it tells how to translate -/// environment-dependent wasm instructions. These functions should not be called by the user. -impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data> { - fn reserve_types(&mut self, num: u32) -> WasmResult<()> { - let num = usize::try_from(num).unwrap(); - self.result.module.types.reserve(num); - self.types.wasm_signatures.reserve(num); - Ok(()) - } fn declare_type_func(&mut self, wasm: WasmFuncType) -> WasmResult<()> { // Deduplicate wasm function signatures through `interned_func_types`, @@ -478,607 +1185,66 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data } } - fn reserve_imports(&mut self, num: u32) -> WasmResult<()> { - Ok(self - .result - .module - .initializers - .reserve(usize::try_from(num).unwrap())) - } - - fn declare_func_import( - &mut self, - index: TypeIndex, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - debug_assert_eq!( - self.result.module.functions.len(), - self.result.module.num_imported_funcs, - "Imported functions must be declared first" - ); - let sig_index = self.result.module.types[index].unwrap_function(); - self.declare_import(module, field, EntityType::Function(sig_index)); - self.result.module.num_imported_funcs += 1; - self.result.debuginfo.wasm_file.imported_func_count += 1; - Ok(()) - } - - fn declare_table_import( - &mut self, - table: Table, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - debug_assert_eq!( - self.result.module.table_plans.len(), - self.result.module.num_imported_tables, - "Imported tables must be declared first" - ); - self.declare_import(module, field, EntityType::Table(table)); - self.result.module.num_imported_tables += 1; - Ok(()) - } - - fn declare_memory_import( - &mut self, - memory: Memory, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - debug_assert_eq!( - self.result.module.memory_plans.len(), - self.result.module.num_imported_memories, - "Imported memories must be declared first" - ); - if memory.shared { - return Err(WasmError::Unsupported("shared memories".to_owned())); - } - self.declare_import(module, field, EntityType::Memory(memory)); - self.result.module.num_imported_memories += 1; - Ok(()) - } - - fn declare_global_import( - &mut self, - global: Global, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - debug_assert_eq!( - self.result.module.globals.len(), - self.result.module.num_imported_globals, - "Imported globals must be declared first" - ); - self.declare_import(module, field, EntityType::Global(global)); - self.result.module.num_imported_globals += 1; - Ok(()) - } - - fn declare_module_import( - &mut self, - ty_index: TypeIndex, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - let signature = self.type_to_module_type(ty_index)?; - self.declare_import(module, field, EntityType::Module(signature)); - Ok(()) - } - - fn declare_instance_import( - &mut self, - ty_index: TypeIndex, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - let signature = self.type_to_instance_type(ty_index)?; - self.declare_import(module, field, EntityType::Instance(signature)); - Ok(()) - } - - fn reserve_func_types(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .functions - .reserve_exact(usize::try_from(num).unwrap()); - self.result - .function_body_inputs - .reserve_exact(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()> { - let sig_index = self.result.module.types[index].unwrap_function(); - self.result.module.functions.push(sig_index); - Ok(()) - } - - fn reserve_tables(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .table_plans - .reserve_exact(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_table(&mut self, table: Table) -> WasmResult<()> { - let plan = TablePlan::for_table(table, &self.tunables); - self.result.module.table_plans.push(plan); - Ok(()) - } - - fn reserve_memories(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .memory_plans - .reserve_exact(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> { - if memory.shared { - return Err(WasmError::Unsupported("shared memories".to_owned())); - } - let plan = MemoryPlan::for_memory(memory, &self.tunables); - self.result.module.memory_plans.push(plan); - Ok(()) - } - - fn reserve_globals(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .globals - .reserve_exact(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_global(&mut self, global: Global) -> WasmResult<()> { - if let GlobalInit::RefFunc(index) = global.initializer { - self.flag_func_possibly_exported(index); - } - self.result.module.globals.push(global); - Ok(()) - } - - fn reserve_exports(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .exports - .reserve(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_func_export(&mut self, func_index: FuncIndex, name: &str) -> WasmResult<()> { - self.flag_func_possibly_exported(func_index); - self.declare_export(EntityIndex::Function(func_index), name) - } - - fn declare_table_export(&mut self, table_index: TableIndex, name: &str) -> WasmResult<()> { - self.declare_export(EntityIndex::Table(table_index), name) - } - - fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &str) -> WasmResult<()> { - self.declare_export(EntityIndex::Memory(memory_index), name) - } - - fn declare_global_export(&mut self, global_index: GlobalIndex, name: &str) -> WasmResult<()> { - self.declare_export(EntityIndex::Global(global_index), name) - } - - fn declare_module_export(&mut self, index: ModuleIndex, name: &str) -> WasmResult<()> { - self.declare_export(EntityIndex::Module(index), name) - } - - fn declare_instance_export(&mut self, index: InstanceIndex, name: &str) -> WasmResult<()> { - self.declare_export(EntityIndex::Instance(index), name) - } - - fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> { - self.flag_func_possibly_exported(func_index); - debug_assert!(self.result.module.start_func.is_none()); - self.result.module.start_func = Some(func_index); - Ok(()) - } - - fn reserve_table_elements(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .table_initializers - .reserve_exact(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_table_elements( - &mut self, - table_index: TableIndex, - base: Option, - offset: u32, - elements: Box<[FuncIndex]>, - ) -> WasmResult<()> { - for element in elements.iter() { - self.flag_func_possibly_exported(*element); - } - self.result - .module - .table_initializers - .push(TableInitializer { - table_index, - base, - offset, - elements, - }); - Ok(()) - } - - fn declare_passive_element( - &mut self, - elem_index: ElemIndex, - segments: Box<[FuncIndex]>, - ) -> WasmResult<()> { - for element in segments.iter() { - self.flag_func_possibly_exported(*element); - } - let index = self.result.module.passive_elements.len(); - self.result.module.passive_elements.push(segments); - let old = self - .result - .module - .passive_elements_map - .insert(elem_index, index); - debug_assert!( - old.is_none(), - "should never get duplicate element indices, that would be a bug in `cranelift_wasm`'s \ - translation" - ); - Ok(()) - } - - fn declare_elements(&mut self, segments: Box<[FuncIndex]>) -> WasmResult<()> { - for element in segments.iter() { - self.flag_func_possibly_exported(*element); - } - Ok(()) - } - - fn reserve_function_bodies(&mut self, _count: u32, offset: u64) { - self.result.debuginfo.wasm_file.code_section_offset = offset; - } - - fn define_function_body( - &mut self, - validator: FuncValidator, - mut body: FunctionBody<'data>, - ) -> WasmResult<()> { - if self.tunables.generate_native_debuginfo { - let func_index = self.result.code_index + self.result.module.num_imported_funcs as u32; - let func_index = FuncIndex::from_u32(func_index); - let sig_index = self.result.module.functions[func_index]; - let sig = &self.types.wasm_signatures[sig_index]; - let mut locals = Vec::new(); - for pair in body.get_locals_reader()? { - locals.push(pair?); - } - self.result - .debuginfo - .wasm_file - .funcs - .push(FunctionMetadata { - locals: locals.into_boxed_slice(), - params: sig.params.iter().cloned().map(|i| i.into()).collect(), - }); - } - body.allow_memarg64(self.features.memory64); - self.result - .function_body_inputs - .push(FunctionBodyData { validator, body }); - self.result.code_index += 1; - Ok(()) - } - - fn reserve_data_initializers(&mut self, num: u32) -> WasmResult<()> { - match &mut self.result.module.memory_initialization { - MemoryInitialization::Segmented(initializers) => { - initializers.reserve_exact(usize::try_from(num).unwrap()) - } - _ => unreachable!(), - } - Ok(()) - } - - fn declare_data_initialization( - &mut self, - memory_index: MemoryIndex, - base: Option, - offset: u64, - data: &'data [u8], - ) -> WasmResult<()> { - match &mut self.result.module.memory_initialization { - MemoryInitialization::Segmented(initializers) => { - initializers.push(MemoryInitializer { - memory_index, - base, - offset, - data: data.into(), - }); - } - _ => unreachable!(), - } - Ok(()) - } - - fn reserve_passive_data(&mut self, _count: u32) -> WasmResult<()> { - // Note: the count passed in here is the *total* segment count - // There is no way to reserve for just the passive segments as they are discovered when iterating the data section entries - // Given that the total segment count might be much larger than the passive count, do not reserve - Ok(()) - } - - fn declare_passive_data(&mut self, data_index: DataIndex, data: &'data [u8]) -> WasmResult<()> { - let index = self.result.module.passive_data.len(); - self.result.module.passive_data.push(Arc::from(data)); - let old = self - .result - .module - .passive_data_map - .insert(data_index, index); - debug_assert!( - old.is_none(), - "a module can't have duplicate indices, this would be a cranelift-wasm bug" - ); - Ok(()) - } - - fn declare_module_name(&mut self, name: &'data str) { - self.result.module.name = Some(name.to_string()); - if self.tunables.generate_native_debuginfo { - self.result.debuginfo.name_section.module_name = Some(name); - } - } - - fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) { - self.result - .module - .func_names - .insert(func_index, name.to_string()); - if self.tunables.generate_native_debuginfo { - self.result - .debuginfo - .name_section - .func_names - .insert(func_index.as_u32(), name); - } - } - - fn declare_local_name(&mut self, func_index: FuncIndex, local: u32, name: &'data str) { - if self.tunables.generate_native_debuginfo { - self.result - .debuginfo - .name_section - .locals_names - .entry(func_index.as_u32()) - .or_insert(HashMap::new()) - .insert(local, name); - } - } - - fn custom_section(&mut self, name: &'data str, data: &'data [u8]) -> WasmResult<()> { - self.register_dwarf_section(name, data); - - match name { - "webidl-bindings" | "wasm-interface-types" => Err(WasmError::Unsupported( - "\ -Support for interface types has temporarily been removed from `wasmtime`. - -For more information about this temoprary you can read on the issue online: - - https://github.com/bytecodealliance/wasmtime/issues/1271 - -and for re-adding support for interface types you can see this issue: - - https://github.com/bytecodealliance/wasmtime/issues/677 -" - .to_owned(), - )), - - // skip other sections - _ => Ok(()), - } - } - - fn wasm_features(&self) -> WasmFeatures { - self.features - } - - fn reserve_modules(&mut self, amount: u32) { - // Go ahead and reserve space in the final `results` array for `amount` - // more modules. - self.modules_to_be += amount as usize; - self.results.reserve(self.modules_to_be); - - // Then also reserve space in our own local module's metadata fields - // we'll be adding to. - self.result.module.modules.reserve(amount as usize); - self.result.module.initializers.reserve(amount as usize); - } - - fn module_start(&mut self) { - // If this is the first time this method is called, nothing to do. - if self.first_module { - self.first_module = false; - return; - } - // Reset our internal state for a new module by saving the current - // module in `results`. - let in_progress = mem::replace(&mut self.result, ModuleTranslation::default()); - self.in_progress.push(in_progress); - self.modules_to_be -= 1; - } - - fn module_end(&mut self) { - self.result.creation_artifacts.shrink_to_fit(); - self.result.creation_modules.shrink_to_fit(); - - let (record_initializer, mut done) = match self.in_progress.pop() { - Some(m) => (true, mem::replace(&mut self.result, m)), - None => (false, mem::take(&mut self.result)), - }; - - if record_initializer { - // Record the type of the module we just finished in our own - // module's list of modules. - let sig = self.gen_type_of_module(&done.module); - self.result.module.modules.push(sig); - - // The root module will store the artifacts for this finished - // module at `artifact_index`. This then needs to be inherited by - // all later modules coming down to our now-current `self.result`... - let mut artifact_index = self.results.len(); - for result in self.in_progress.iter_mut().chain(Some(&mut self.result)) { - result.creation_artifacts.push(artifact_index); - artifact_index = result.creation_artifacts.len() - 1; - } - // ... and then `self.result` needs to create a new module with - // whatever was record to save off as its own artifacts/modules. - self.result - .module - .initializers - .push(Initializer::CreateModule { - artifact_index, - artifacts: mem::take(&mut done.creation_artifacts), - modules: mem::take(&mut done.creation_modules), - }); - } - - // And the final step is to insert the module into the list of finished - // modules to get returned at the end. - self.results.push(done); - } - - fn reserve_instances(&mut self, amt: u32) { - self.result.module.instances.reserve(amt as usize); - self.result.module.initializers.reserve(amt as usize); - } - - fn declare_instance( - &mut self, - module: ModuleIndex, - args: Vec<(&'data str, EntityIndex)>, - ) -> WasmResult<()> { - let args = args.into_iter().map(|(s, i)| (s.to_string(), i)).collect(); - // Record the type of this instance with the type signature of the - // module we're instantiating and then also add an initializer which - // records that we'll be adding to the instance index space here. - let module_ty = self.result.module.modules[module]; - let instance_ty = self.types.module_signatures[module_ty].exports; - self.result.module.instances.push(instance_ty); - self.result - .module - .initializers - .push(Initializer::Instantiate { module, args }); - Ok(()) - } - - fn declare_alias(&mut self, alias: Alias) -> WasmResult<()> { - match alias { - // Types are easy, we statically know everything so we're just - // copying some pointers from our parent module to our own module. - // - // Note that we don't add an initializer for this alias because - // we statically know where all types point to. - Alias::OuterType { - relative_depth, - index, - } => { - let module_idx = self.in_progress.len() - 1 - (relative_depth as usize); - let ty = self.in_progress[module_idx].module.types[index]; - self.result.module.types.push(ty); - } - - // Modules are a bit trickier since we need to record how to track - // the state from the original module down to our own. - Alias::OuterModule { - relative_depth, - index, - } => { - // First we can copy the type from the parent module into our - // own module to record what type our module definition will - // have. - let module_idx = self.in_progress.len() - 1 - (relative_depth as usize); - let module_ty = self.in_progress[module_idx].module.modules[index]; - self.result.module.modules.push(module_ty); - - // Next we'll be injecting a module value that is closed over, - // and that will be used to define the module into the index - // space. Record an initializer about where our module is - // sourced from (which will be stored within each module value - // itself). - let module_index = self.result.creation_modules.len(); - self.result - .module - .initializers - .push(Initializer::DefineModule(module_index)); - - // And finally we need to record a breadcrumb trail of how to - // get the module value into `module_index`. The module just - // after our destination module will use a `ModuleIndex` to - // fetch the module value, and everything else inbetween will - // inherit that module's closed-over value. - let mut upvar = ModuleUpvar::Local(index); - for outer in self.in_progress[module_idx + 1..].iter_mut() { - let upvar = mem::replace( - &mut upvar, - ModuleUpvar::Inherit(outer.creation_modules.len()), - ); - outer.creation_modules.push(upvar); + /// Parses the Name section of the wasm module. + fn name_section(&mut self, names: NameSectionReader<'data>) -> WasmResult<()> { + for subsection in names { + match subsection? { + wasmparser::Name::Function(f) => { + let mut names = f.get_map()?; + for _ in 0..names.get_count() { + let Naming { index, name } = names.read()?; + let index = FuncIndex::from_u32(index); + self.result + .module + .func_names + .insert(index, name.to_string()); + if self.tunables.generate_native_debuginfo { + self.result + .debuginfo + .name_section + .func_names + .insert(index.as_u32(), name); + } + } } - self.result.creation_modules.push(upvar); - } - - // This case is slightly more involved, we'll be recording all the - // type information for each kind of entity, and then we also need - // to record an initialization step to get the export from the - // instance. - Alias::InstanceExport { instance, export } => { - let ty = self.result.module.instances[instance]; - match &self.types.instance_signatures[ty].exports[export] { - EntityType::Global(g) => { - self.result.module.globals.push(g.clone()); - self.result.module.num_imported_globals += 1; + wasmparser::Name::Module(module) => { + let name = module.get_name()?; + self.result.module.name = Some(name.to_string()); + if self.tunables.generate_native_debuginfo { + self.result.debuginfo.name_section.module_name = Some(name); } - EntityType::Memory(mem) => { - let plan = MemoryPlan::for_memory(*mem, &self.tunables); - self.result.module.memory_plans.push(plan); - self.result.module.num_imported_memories += 1; - } - EntityType::Table(t) => { - let plan = TablePlan::for_table(*t, &self.tunables); - self.result.module.table_plans.push(plan); - self.result.module.num_imported_tables += 1; - } - EntityType::Function(sig) => { - self.result.module.functions.push(*sig); - self.result.module.num_imported_funcs += 1; - self.result.debuginfo.wasm_file.imported_func_count += 1; - } - EntityType::Instance(sig) => { - self.result.module.instances.push(*sig); - } - EntityType::Module(sig) => { - self.result.module.modules.push(*sig); - } - EntityType::Tag(_) => unimplemented!(), } - self.result - .module - .initializers - .push(Initializer::AliasInstanceExport { - instance, - export: export.to_string(), - }) + wasmparser::Name::Local(l) => { + if !self.tunables.generate_native_debuginfo { + continue; + } + let mut reader = l.get_indirect_map()?; + for _ in 0..reader.get_indirect_count() { + let f = reader.read()?; + let mut map = f.get_map()?; + for _ in 0..map.get_count() { + let Naming { index, name } = map.read()?; + + self.result + .debuginfo + .name_section + .locals_names + .entry(f.indirect_index) + .or_insert(HashMap::new()) + .insert(index, name); + } + } + } + wasmparser::Name::Label(_) + | wasmparser::Name::Type(_) + | wasmparser::Name::Table(_) + | wasmparser::Name::Global(_) + | wasmparser::Name::Memory(_) + | wasmparser::Name::Element(_) + | wasmparser::Name::Data(_) + | wasmparser::Name::Unknown { .. } => {} } } - Ok(()) } }