diff --git a/Cargo.lock b/Cargo.lock index 084a2cb0a2..f6cf7f0f26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,7 +388,7 @@ dependencies = [ "souper-ir", "target-lexicon", "thiserror", - "wast 29.0.0", + "wast 31.0.0", ] [[package]] @@ -1393,7 +1393,7 @@ dependencies = [ "peepmatic-test-operator", "peepmatic-traits", "serde", - "wast 29.0.0", + "wast 31.0.0", "z3", ] @@ -1421,7 +1421,7 @@ dependencies = [ "peepmatic-traits", "rand", "serde", - "wast 29.0.0", + "wast 31.0.0", ] [[package]] @@ -1446,7 +1446,7 @@ dependencies = [ "serde", "serde_test", "thiserror", - "wast 29.0.0", + "wast 31.0.0", ] [[package]] @@ -1458,7 +1458,7 @@ dependencies = [ "peepmatic", "peepmatic-test-operator", "souper-ir", - "wast 29.0.0", + "wast 31.0.0", ] [[package]] @@ -1479,7 +1479,7 @@ version = "0.69.0" dependencies = [ "peepmatic-traits", "serde", - "wast 29.0.0", + "wast 31.0.0", ] [[package]] @@ -2350,20 +2350,21 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed89eaf99e08b84f96e477a16588a07dd3b51dc5f07291c3706782f62a10a5e1" +checksum = "c75fa62cf1464aa6655479ae454202a159cc82b7b4d66e8f174409669c0654c5" dependencies = [ "leb128", ] [[package]] name = "wasm-smith" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509904d9c4c4659ac238a3f27c3656dd6d3931697eddd4b0f32e335769c298d0" +checksum = "0b9b9b796bf4da5eb0523b136d6f0cc9a59c16a66ece8e0d5a14a9cdccf2864e" dependencies = [ "arbitrary", + "indexmap", "leb128", "wasm-encoder", ] @@ -2394,15 +2395,15 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.71.0" +version = "0.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a30c99437829ede826802bfcf28500cf58df00e66cb9114df98813bc145ff1" +checksum = "2cdf4d872d407f9fb44956e540582eeaf0dc4fb8142f1f0f64e2c37196bada01" [[package]] name = "wasmprinter" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0515db67c610037f3c53ec36976edfd1eb01bac6b1226914b17ce609480e729f" +checksum = "a0c139586e3b80b899f5aaaa3720c7a236ea9073e315292e01d099da12efacc5" dependencies = [ "anyhow", "wasmparser", @@ -2754,7 +2755,7 @@ version = "0.22.0" dependencies = [ "anyhow", "wasmtime", - "wast 29.0.0", + "wast 31.0.0", ] [[package]] @@ -2790,29 +2791,20 @@ dependencies = [ [[package]] name = "wast" -version = "29.0.0" +version = "31.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf2268937131d63c3d833242bf5e075406f9ed868b4265f3280e15dac29ac18" -dependencies = [ - "leb128", -] - -[[package]] -name = "wast" -version = "30.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b79907b22f740634810e882d8d1d9d0f9563095a8ab94e786e370242bff5cd2" +checksum = "9beb1f6b63f08c523a1e8e76fc70058af4d2a34ef1c504f56cdac7b6970228b9" dependencies = [ "leb128", ] [[package]] name = "wat" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8279a02835bf12e61ed2b3c3cbc6ecf9918762fd97e036917c11a09ec20ca44" +checksum = "2a0b3044da73d3b84a822d955afad356759b2fee454b6882722008dace80b68e" dependencies = [ - "wast 30.0.0", + "wast 31.0.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 54f87e181c..640189732d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,12 +38,12 @@ anyhow = "1.0.19" target-lexicon = { version = "0.11.0", default-features = false } pretty_env_logger = "0.4.0" file-per-thread-logger = "0.1.1" -wat = "1.0.30" +wat = "1.0.32" libc = "0.2.60" log = "0.4.8" rayon = "1.2.1" humantime = "2.0.0" -wasmparser = "0.71.0" +wasmparser = "0.72.0" [dev-dependencies] env_logger = "0.8.1" diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml index d8a8baf384..f601965022 100644 --- a/cranelift/codegen/Cargo.toml +++ b/cranelift/codegen/Cargo.toml @@ -30,7 +30,7 @@ peepmatic-traits = { path = "../peepmatic/crates/traits", optional = true, versi peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.69.0" } regalloc = { version = "0.0.31" } souper-ir = { version = "2.1.0", optional = true } -wast = { version = "29.0.0", optional = true } +wast = { version = "31.0.0", optional = true } # It is a goal of the cranelift-codegen crate to have minimal external dependencies. # Please don't add any unless they are essential to the task of creating binary # machine code. Integration tests that need external dependencies can be diff --git a/cranelift/peepmatic/Cargo.toml b/cranelift/peepmatic/Cargo.toml index 9028e7105a..dcb1da1537 100644 --- a/cranelift/peepmatic/Cargo.toml +++ b/cranelift/peepmatic/Cargo.toml @@ -15,7 +15,7 @@ peepmatic-macro = { version = "0.69.0", path = "crates/macro" } peepmatic-runtime = { version = "0.69.0", path = "crates/runtime", features = ["construct"] } peepmatic-traits = { version = "0.69.0", path = "crates/traits" } serde = { version = "1.0.105", features = ["derive"] } -wast = "29.0.0" +wast = "31.0.0" z3 = { version = "0.7.1", features = ["static-link-z3"] } [dev-dependencies] diff --git a/cranelift/peepmatic/crates/fuzzing/Cargo.toml b/cranelift/peepmatic/crates/fuzzing/Cargo.toml index 1611b75c6d..fda6532563 100644 --- a/cranelift/peepmatic/crates/fuzzing/Cargo.toml +++ b/cranelift/peepmatic/crates/fuzzing/Cargo.toml @@ -21,4 +21,4 @@ peepmatic-test-operator = { path = "../test-operator" } peepmatic-traits = { path = "../traits" } rand = { version = "0.7.3", features = ["small_rng"] } serde = "1.0.106" -wast = "29.0.0" +wast = "31.0.0" diff --git a/cranelift/peepmatic/crates/runtime/Cargo.toml b/cranelift/peepmatic/crates/runtime/Cargo.toml index 8e15ad7527..ea1909c282 100644 --- a/cranelift/peepmatic/crates/runtime/Cargo.toml +++ b/cranelift/peepmatic/crates/runtime/Cargo.toml @@ -16,7 +16,7 @@ peepmatic-automata = { version = "0.69.0", path = "../automata", features = ["se peepmatic-traits = { version = "0.69.0", path = "../traits" } serde = { version = "1.0.105", features = ["derive"] } thiserror = "1.0.15" -wast = { version = "29.0.0", optional = true } +wast = { version = "31.0.0", optional = true } [dev-dependencies] peepmatic-test-operator = { version = "0.69.0", path = "../test-operator" } diff --git a/cranelift/peepmatic/crates/souper/Cargo.toml b/cranelift/peepmatic/crates/souper/Cargo.toml index 811363651c..7b512526d6 100644 --- a/cranelift/peepmatic/crates/souper/Cargo.toml +++ b/cranelift/peepmatic/crates/souper/Cargo.toml @@ -16,4 +16,4 @@ log = "0.4.8" [dev-dependencies] peepmatic = { path = "../..", version = "0.69.0" } peepmatic-test-operator = { version = "0.69.0", path = "../test-operator" } -wast = "29.0.0" +wast = "31.0.0" diff --git a/cranelift/peepmatic/crates/test-operator/Cargo.toml b/cranelift/peepmatic/crates/test-operator/Cargo.toml index 295610c1bc..de23fe59fd 100644 --- a/cranelift/peepmatic/crates/test-operator/Cargo.toml +++ b/cranelift/peepmatic/crates/test-operator/Cargo.toml @@ -11,4 +11,4 @@ edition = "2018" [dependencies] peepmatic-traits = { version = "0.69.0", path = "../traits" } serde = { version = "1.0.105", features = ["derive"] } -wast = "29.0.0" +wast = "31.0.0" diff --git a/cranelift/wasm/Cargo.toml b/cranelift/wasm/Cargo.toml index 413d2ee004..ac7866af70 100644 --- a/cranelift/wasm/Cargo.toml +++ b/cranelift/wasm/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["webassembly", "wasm"] edition = "2018" [dependencies] -wasmparser = { version = "0.71", default-features = false } +wasmparser = { version = "0.72", default-features = false } cranelift-codegen = { path = "../codegen", version = "0.69.0", default-features = false } cranelift-entity = { path = "../entity", version = "0.69.0" } cranelift-frontend = { path = "../frontend", version = "0.69.0", default-features = false } diff --git a/cranelift/wasm/src/environ/spec.rs b/cranelift/wasm/src/environ/spec.rs index f7577de5d3..84978dc0d8 100644 --- a/cranelift/wasm/src/environ/spec.rs +++ b/cranelift/wasm/src/environ/spec.rs @@ -205,25 +205,34 @@ pub enum ReturnMode { /// An entry in the alias section of a wasm module (from the module linking /// proposal) -pub enum Alias { - /// A parent's module is being aliased into our own index space. - /// - /// Note that the index here is in the parent's index space, not our own. - ParentModule(ModuleIndex), +pub enum Alias<'a> { + /// An outer module's module is being aliased into our own index space. + OuterModule { + /// The number of modules above us that we're referencing. + relative_depth: u32, + /// The module index in the outer module's index space we're referencing. + index: ModuleIndex, + }, - /// A parent's type is being aliased into our own index space + /// An outer module's type is being aliased into our own index space /// - /// Note that the index here is in the parent's index space, not our own. - ParentType(TypeIndex), + /// Note that the index here is in the outer module's index space, not our + /// own. + OuterType { + /// The number of modules above us that we're referencing. + relative_depth: u32, + /// The type index in the outer module's index space we're referencing. + index: TypeIndex, + }, /// A previously created instance is having one of its exports aliased into /// our index space. - Child { + InstanceExport { /// The index we're aliasing. instance: InstanceIndex, /// The nth export that we're inserting into our own index space /// locally. - export: usize, + export: &'a str, }, } @@ -1014,30 +1023,15 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment { drop(amount); } - /// Declares that a module will come later with the type signature provided. - fn declare_module(&mut self, ty: TypeIndex) -> WasmResult<()> { - drop(ty); - Err(WasmError::Unsupported("module linking".to_string())) - } - /// 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); - } + fn module_start(&mut self) {} /// 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); - } + fn module_end(&mut self) {} /// Indicates that this module will have `amount` instances. fn reserve_instances(&mut self, amount: u32) { @@ -1046,7 +1040,11 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment { /// Declares a new instance which this module will instantiate before it's /// instantiated. - fn declare_instance(&mut self, module: ModuleIndex, args: Vec) -> WasmResult<()> { + fn declare_instance( + &mut self, + module: ModuleIndex, + args: Vec<(&'data str, EntityIndex)>, + ) -> WasmResult<()> { drop((module, args)); Err(WasmError::Unsupported("wasm instance".to_string())) } @@ -1056,7 +1054,7 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment { /// The alias comes from the `instance` specified (or the parent if `None` /// is supplied) and the index is either in the module's own index spaces /// for the parent or an index into the exports for nested instances. - fn declare_alias(&mut self, alias: Alias) -> WasmResult<()> { + fn declare_alias(&mut self, alias: Alias<'data>) -> WasmResult<()> { drop(alias); Err(WasmError::Unsupported("wasm alias".to_string())) } diff --git a/cranelift/wasm/src/module_translator.rs b/cranelift/wasm/src/module_translator.rs index c3e27dd820..ed71d7ce1c 100644 --- a/cranelift/wasm/src/module_translator.rs +++ b/cranelift/wasm/src/module_translator.rs @@ -4,8 +4,8 @@ use crate::environ::{ModuleEnvironment, WasmResult}; use crate::sections_translator::{ parse_alias_section, parse_data_section, parse_element_section, parse_event_section, parse_export_section, parse_function_section, parse_global_section, parse_import_section, - parse_instance_section, parse_memory_section, parse_module_section, parse_name_section, - parse_start_section, parse_table_section, parse_type_section, + parse_instance_section, parse_memory_section, parse_name_section, parse_start_section, + parse_table_section, parse_type_section, }; use crate::state::ModuleTranslationState; use cranelift_codegen::timing; @@ -22,23 +22,16 @@ pub fn translate_module<'data>( let mut module_translation_state = ModuleTranslationState::new(); let mut validator = Validator::new(); validator.wasm_features(environ.wasm_features()); - let mut stack = Vec::new(); - let mut modules = 1; - let mut cur_module = 0; for payload in Parser::new(0).parse_all(data) { match payload? { Payload::Version { num, range } => { validator.version(num, &range)?; - environ.module_start(cur_module); + environ.module_start(); } Payload::End => { validator.end()?; - environ.module_end(cur_module); - if let Some((other, other_index)) = stack.pop() { - validator = other; - cur_module = other_index; - } + environ.module_end(); } Payload::TypeSection(types) => { @@ -111,10 +104,6 @@ pub fn translate_module<'data>( environ.reserve_passive_data(count)?; } - Payload::ModuleSection(s) => { - validator.module_section(&s)?; - parse_module_section(s, environ)?; - } Payload::InstanceSection(s) => { validator.instance_section(&s)?; parse_instance_section(s, environ)?; @@ -123,20 +112,17 @@ pub fn translate_module<'data>( validator.alias_section(&s)?; parse_alias_section(s, environ)?; } - Payload::ModuleCodeSectionStart { + Payload::ModuleSectionStart { count, range, size: _, } => { - validator.module_code_section_start(count, &range)?; + validator.module_section_start(count, &range)?; + environ.reserve_modules(count); } - Payload::ModuleCodeSectionEntry { .. } => { - let subvalidator = validator.module_code_section_entry(); - stack.push((validator, cur_module)); - validator = subvalidator; - cur_module = modules; - modules += 1; + Payload::ModuleSectionEntry { .. } => { + validator.module_section_entry(); } Payload::CustomSection { diff --git a/cranelift/wasm/src/sections_translator.rs b/cranelift/wasm/src/sections_translator.rs index 0e791c1a43..3906c02393 100644 --- a/cranelift/wasm/src/sections_translator.rs +++ b/cranelift/wasm/src/sections_translator.rs @@ -504,19 +504,6 @@ pub fn parse_name_section<'data>( Ok(()) } -/// Parses the Module section of the wasm module. -pub fn parse_module_section<'data>( - section: wasmparser::ModuleSectionReader<'data>, - environ: &mut dyn ModuleEnvironment<'data>, -) -> WasmResult<()> { - environ.reserve_modules(section.get_count()); - - for module_ty in section { - environ.declare_module(TypeIndex::from_u32(module_ty?))?; - } - Ok(()) -} - /// Parses the Instance section of the wasm module. pub fn parse_instance_section<'data>( section: wasmparser::InstanceSectionReader<'data>, @@ -530,20 +517,23 @@ pub fn parse_instance_section<'data>( let args = instance .args()? .into_iter() - .map(|result| { - let (kind, idx) = result?; - Ok(match kind { - ExternalKind::Function => EntityIndex::Function(FuncIndex::from_u32(idx)), - ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(idx)), - ExternalKind::Memory => EntityIndex::Memory(MemoryIndex::from_u32(idx)), - ExternalKind::Global => EntityIndex::Global(GlobalIndex::from_u32(idx)), - ExternalKind::Module => EntityIndex::Module(ModuleIndex::from_u32(idx)), - ExternalKind::Instance => EntityIndex::Instance(InstanceIndex::from_u32(idx)), + .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)) + } ExternalKind::Event => unimplemented!(), // this won't pass validation ExternalKind::Type => unreachable!(), - }) + }; + Ok((arg.name, index)) }) .collect::>>()?; environ.declare_instance(module, args)?; @@ -557,19 +547,28 @@ pub fn parse_alias_section<'data>( environ: &mut dyn ModuleEnvironment<'data>, ) -> WasmResult<()> { for alias in section { - let alias = alias?; - let alias = match alias.instance { - wasmparser::AliasedInstance::Parent => { - match alias.kind { - ExternalKind::Module => Alias::ParentModule(ModuleIndex::from_u32(alias.index)), - ExternalKind::Type => Alias::ParentType(TypeIndex::from_u32(alias.index)), - // shouldn't get past validation - _ => unreachable!(), - } - } - wasmparser::AliasedInstance::Child(i) => Alias::Child { - instance: InstanceIndex::from_u32(i), - export: alias.index as usize, + let alias = match alias? { + wasmparser::Alias::OuterType { + relative_depth, + index, + } => Alias::OuterType { + relative_depth, + index: TypeIndex::from_u32(index), + }, + wasmparser::Alias::OuterModule { + relative_depth, + index, + } => Alias::OuterModule { + relative_depth, + index: ModuleIndex::from_u32(index), + }, + wasmparser::Alias::InstanceExport { + instance, + export, + kind: _, + } => Alias::InstanceExport { + instance: InstanceIndex::from_u32(instance), + export, }, }; environ.declare_alias(alias)?; diff --git a/crates/debug/Cargo.toml b/crates/debug/Cargo.toml index 523c136cba..122c22b930 100644 --- a/crates/debug/Cargo.toml +++ b/crates/debug/Cargo.toml @@ -13,7 +13,7 @@ edition = "2018" [dependencies] gimli = "0.23.0" -wasmparser = "0.71" +wasmparser = "0.72" object = { version = "0.22.0", default-features = false, features = ["read_core", "elf", "write"] } wasmtime-environ = { path = "../environ", version = "0.22.0" } target-lexicon = { version = "0.11.0", default-features = false } diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml index caa6ba3b58..3463bc4d30 100644 --- a/crates/environ/Cargo.toml +++ b/crates/environ/Cargo.toml @@ -16,7 +16,7 @@ anyhow = "1.0" cranelift-codegen = { path = "../../cranelift/codegen", version = "0.69.0", features = ["enable-serde"] } cranelift-entity = { path = "../../cranelift/entity", version = "0.69.0", features = ["enable-serde"] } cranelift-wasm = { path = "../../cranelift/wasm", version = "0.69.0", features = ["enable-serde"] } -wasmparser = "0.71" +wasmparser = "0.72" indexmap = { version = "1.0.2", features = ["serde-1"] } thiserror = "1.0.4" serde = { version = "1.0.94", features = ["derive"] } diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs index 226d96134d..644f1a2bb3 100644 --- a/crates/environ/src/module.rs +++ b/crates/environ/src/module.rs @@ -142,12 +142,6 @@ impl ModuleType { /// memory initializers. #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct Module { - /// The parent index of this module, used for the module linking proposal. - /// - /// This index is into the list of modules returned from compilation of a - /// single wasm file with nested modules. - pub parent: Option, - /// The name of this wasm module, often found in the wasm file. pub name: Option, @@ -213,25 +207,26 @@ pub struct Module { pub enum Initializer { /// An imported item is required to be provided. Import { - /// Module name of this import - module: String, - /// Optional field name of this import + /// Name of this import + name: String, + /// The field name projection of this import. When module-linking is + /// enabled this is always `None`. Otherwise this is always `Some`. field: Option, /// Where this import will be placed, which also has type information /// about the import. index: EntityIndex, }, - /// A module from the parent's declared modules is inserted into our own + /// An export from a previously defined instance is being inserted into our /// index space. - AliasParentModule(ModuleIndex), - - /// A module from the parent's declared modules is inserted into our own - /// index space. - #[allow(missing_docs)] + /// + /// Note that when the module linking proposal is enabled two-level imports + /// will implicitly desugar to this initializer. AliasInstanceExport { + /// The instance that we're referencing. instance: InstanceIndex, - export: usize, + /// Which export is being inserted into our index space. + export: String, }, /// A module is being instantiated with previously configured intializers @@ -239,8 +234,9 @@ pub enum Initializer { Instantiate { /// The module that this instance is instantiating. module: ModuleIndex, - /// The arguments provided to instantiation. - args: Vec, + /// The arguments provided to instantiation, along with their name in + /// the instance being instantiated. + args: IndexMap, }, /// A module is defined into the module index space, and which module is @@ -351,11 +347,9 @@ impl Module { /// module name, field name, and type that's being imported. pub fn imports(&self) -> impl Iterator, EntityType)> { self.initializers.iter().filter_map(move |i| match i { - Initializer::Import { - module, - field, - index, - } => Some((module.as_str(), field.as_deref(), self.type_of(*index))), + Initializer::Import { name, field, index } => { + Some((name.as_str(), field.as_deref(), self.type_of(*index))) + } _ => None, }) } @@ -389,16 +383,16 @@ pub struct TypeTables { /// The type signature of known modules. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ModuleSignature { - /// All imports in this module, listed in order with their module/name and + /// All imports in this module, listed in order with their name and /// what type they're importing. - pub imports: Vec<(String, Option, EntityType)>, + pub imports: IndexMap, /// Exports are what an instance type conveys, so we go through an /// indirection over there. pub exports: InstanceTypeIndex, } /// The type signature of known instances. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct InstanceSignature { /// The name of what's being exported as well as its type signature. pub exports: IndexMap, diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs index 7b2cca6110..7bb30ea166 100644 --- a/crates/environ/src/module_environ.rs +++ b/crates/environ/src/module_environ.rs @@ -14,7 +14,7 @@ use cranelift_wasm::{ WasmError, WasmFuncType, WasmResult, }; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::collections::{hash_map::Entry, HashMap}; use std::convert::TryFrom; use std::mem; use std::path::PathBuf; @@ -31,6 +31,10 @@ pub struct ModuleEnvironment<'data> { /// the module linking proposal. results: Vec>, + /// Modules which are in-progress being translated, or otherwise also known + /// as the outer modules of the current module being processed. + in_progress: Vec>, + /// How many modules that have not yet made their way into `results` which /// are coming at some point. modules_to_be: usize, @@ -38,13 +42,11 @@ pub struct ModuleEnvironment<'data> { /// Intern'd types for this entire translation, shared by all modules. types: TypeTables, - /// Where our module will get pushed into `results` after it's finished. - cur: usize, - // Various bits and pieces of configuration features: WasmFeatures, target_config: TargetFrontendConfig, tunables: Tunables, + first_module: bool, } /// The result of translating via `ModuleEnvironment`. Function bodies are not @@ -72,15 +74,7 @@ pub struct ModuleTranslation<'data> { /// which function is currently being defined. code_index: u32, - /// When local modules are declared an entry is pushed onto this list which - /// indicates that the initializer at the specified position needs to be - /// rewritten with the module's final index in the global list of compiled - /// modules. - module_initializer_indexes: Vec, - - /// Used as a pointer into the above list as the module code section is - /// parsed. - num_modules_defined: usize, + implicit_instances: HashMap<&'data str, InstanceIndex>, } /// Contains function data: byte code and its offset in the module. @@ -142,12 +136,13 @@ impl<'data> ModuleEnvironment<'data> { Self { result: ModuleTranslation::default(), results: Vec::with_capacity(1), + in_progress: Vec::new(), modules_to_be: 1, - cur: 0, types: Default::default(), target_config, tunables: tunables.clone(), features: *features, + first_module: true, } } @@ -163,7 +158,8 @@ impl<'data> ModuleEnvironment<'data> { /// Note that for MVP modules this will always be a list with one element, /// but with the module linking proposal this may have many elements. /// - /// For the module linking proposal the top-level module is at index 0. + /// For the module linking proposal the top-level module is returned as the + /// first return value. /// /// The `TypeTables` structure returned contains intern'd versions of types /// referenced from each module translation. This primarily serves as the @@ -173,10 +169,10 @@ impl<'data> ModuleEnvironment<'data> { pub fn translate( mut self, data: &'data [u8], - ) -> WasmResult<(Vec>, TypeTables)> { + ) -> WasmResult<(usize, Vec>, TypeTables)> { translate_module(data, &mut self)?; assert!(self.results.len() > 0); - Ok((self.results, self.types)) + Ok((self.results.len() - 1, self.results, self.types)) } fn declare_export(&mut self, export: EntityIndex, name: &str) -> WasmResult<()> { @@ -224,6 +220,137 @@ impl<'data> ModuleEnvironment<'data> { dwarf.ranges = gimli::RangeLists::new(info.debug_ranges, info.debug_rnglists); dwarf.locations = gimli::LocationLists::new(info.debug_loc, info.debug_loclists); } + + /// Declares a new import with the `module` and `field` names, importing the + /// `ty` specified. + /// + /// Note that this method is somewhat tricky due to the implementation of + /// the module linking proposal. In the module linking proposal two-level + /// imports are recast as single-level imports of instances. That recasting + /// happens here by recording an import of an instance for the first time + /// we see a two-level import. + /// + /// When the module linking proposal is disabled, however, disregard this + /// logic and instead work directly with two-level imports since no + /// instances are defined. + fn declare_import(&mut self, module: &'data str, field: Option<&'data str>, ty: EntityType) { + if !self.features.module_linking { + assert!(field.is_some()); + let index = self.push_type(ty); + self.result.module.initializers.push(Initializer::Import { + name: module.to_owned(), + field: field.map(|s| s.to_string()), + index, + }); + return; + } + + match field { + Some(field) => { + // If this is a two-level import then this is actually an + // implicit import of an instance, where each two-level import + // is an alias directive from the original instance. The first + // thing we do here is lookup our implicit instance, creating a + // blank one if it wasn't already created. + let instance = match self.result.implicit_instances.entry(module) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(v) => { + let ty = self + .types + .instance_signatures + .push(InstanceSignature::default()); + let idx = self.result.module.instances.push(ty); + self.result.module.initializers.push(Initializer::Import { + name: module.to_owned(), + field: None, + index: EntityIndex::Instance(idx), + }); + *v.insert(idx) + } + }; + + // Update the implicit instance's type signature with this new + // field and its type. + self.types.instance_signatures[self.result.module.instances[instance]] + .exports + .insert(field.to_string(), ty.clone()); + + // Record our implicit alias annotation which corresponds to + // this import that we're processing. + self.result + .module + .initializers + .push(Initializer::AliasInstanceExport { + instance, + export: field.to_string(), + }); + + // And then record the type information for the item that we're + // processing. + self.push_type(ty); + } + None => { + // Without a field then this is a single-level import (a feature + // of module linking) which means we're simply importing that + // name with the specified type. Record the type information and + // then the name that we're importing. + let index = self.push_type(ty); + self.result.module.initializers.push(Initializer::Import { + name: module.to_owned(), + field: None, + index, + }); + } + } + } + + fn push_type(&mut self, ty: EntityType) -> EntityIndex { + match ty { + EntityType::Function(ty) => { + EntityIndex::Function(self.result.module.functions.push(ty)) + } + EntityType::Table(ty) => { + let plan = TablePlan::for_table(ty, &self.tunables); + EntityIndex::Table(self.result.module.table_plans.push(plan)) + } + EntityType::Memory(ty) => { + let plan = MemoryPlan::for_memory(ty, &self.tunables); + EntityIndex::Memory(self.result.module.memory_plans.push(plan)) + } + EntityType::Global(ty) => EntityIndex::Global(self.result.module.globals.push(ty)), + EntityType::Instance(ty) => { + EntityIndex::Instance(self.result.module.instances.push(ty)) + } + EntityType::Module(ty) => EntityIndex::Module(self.result.module.modules.push(ty)), + EntityType::Event(_) => unimplemented!(), + } + } + + fn gen_type_of_module(&mut self, module: usize) -> ModuleTypeIndex { + let module = &self.results[module].module; + let imports = module + .imports() + .map(|(s, field, ty)| { + assert!(field.is_none()); + (s.to_string(), ty) + }) + .collect(); + let exports = module + .exports + .iter() + .map(|(name, idx)| (name.clone(), module.type_of(*idx))) + .collect(); + + // FIXME(#2469): this instance/module signature insertion should likely + // be deduplicated. + let exports = self + .types + .instance_signatures + .push(InstanceSignature { exports }); + self.types + .module_signatures + .push(ModuleSignature { imports, exports }) + } } impl<'data> TargetEnvironment for ModuleEnvironment<'data> { @@ -267,13 +394,29 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data fn declare_type_module( &mut self, - imports: &[(&'data str, Option<&'data str>, EntityType)], + declared_imports: &[(&'data str, Option<&'data str>, EntityType)], exports: &[(&'data str, EntityType)], ) -> WasmResult<()> { - let imports = imports - .iter() - .map(|i| (i.0.to_string(), i.1.map(|s| s.to_string()), i.2.clone())) - .collect(); + let mut imports = indexmap::IndexMap::new(); + let mut instance_types = HashMap::new(); + for (module, field, ty) in declared_imports { + match field { + Some(field) => { + let idx = *instance_types + .entry(module) + .or_insert_with(|| self.types.instance_signatures.push(Default::default())); + self.types.instance_signatures[idx] + .exports + .insert(field.to_string(), ty.clone()); + if !imports.contains_key(*module) { + imports.insert(module.to_string(), EntityType::Instance(idx)); + } + } + None => { + imports.insert(module.to_string(), ty.clone()); + } + } + } let exports = exports .iter() .map(|e| (e.0.to_string(), e.1.clone())) @@ -344,8 +487,8 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data fn declare_func_import( &mut self, index: TypeIndex, - module: &str, - field: Option<&str>, + module: &'data str, + field: Option<&'data str>, ) -> WasmResult<()> { debug_assert_eq!( self.result.module.functions.len(), @@ -353,12 +496,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data "Imported functions must be declared first" ); let sig_index = self.result.module.types[index].unwrap_function(); - let func_index = self.result.module.functions.push(sig_index); - self.result.module.initializers.push(Initializer::Import { - module: module.to_owned(), - field: field.map(|s| s.to_owned()), - index: EntityIndex::Function(func_index), - }); + 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(()) @@ -367,21 +505,15 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data fn declare_table_import( &mut self, table: Table, - module: &str, - field: Option<&str>, + 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" ); - let plan = TablePlan::for_table(table, &self.tunables); - let table_index = self.result.module.table_plans.push(plan); - self.result.module.initializers.push(Initializer::Import { - module: module.to_owned(), - field: field.map(|s| s.to_owned()), - index: EntityIndex::Table(table_index), - }); + self.declare_import(module, field, EntityType::Table(table)); self.result.module.num_imported_tables += 1; Ok(()) } @@ -389,8 +521,8 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data fn declare_memory_import( &mut self, memory: Memory, - module: &str, - field: Option<&str>, + module: &'data str, + field: Option<&'data str>, ) -> WasmResult<()> { debug_assert_eq!( self.result.module.memory_plans.len(), @@ -400,13 +532,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data if memory.shared { return Err(WasmError::Unsupported("shared memories".to_owned())); } - let plan = MemoryPlan::for_memory(memory, &self.tunables); - let memory_index = self.result.module.memory_plans.push(plan); - self.result.module.initializers.push(Initializer::Import { - module: module.to_owned(), - field: field.map(|s| s.to_owned()), - index: EntityIndex::Memory(memory_index), - }); + self.declare_import(module, field, EntityType::Memory(memory)); self.result.module.num_imported_memories += 1; Ok(()) } @@ -414,20 +540,15 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data fn declare_global_import( &mut self, global: Global, - module: &str, - field: Option<&str>, + 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" ); - let global_index = self.result.module.globals.push(global); - self.result.module.initializers.push(Initializer::Import { - module: module.to_owned(), - field: field.map(|s| s.to_owned()), - index: EntityIndex::Global(global_index), - }); + self.declare_import(module, field, EntityType::Global(global)); self.result.module.num_imported_globals += 1; Ok(()) } @@ -439,12 +560,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data field: Option<&'data str>, ) -> WasmResult<()> { let signature = self.type_to_module_type(ty_index)?; - let module_index = self.result.module.modules.push(signature); - self.result.module.initializers.push(Initializer::Import { - module: module.to_owned(), - field: field.map(|s| s.to_owned()), - index: EntityIndex::Module(module_index), - }); + self.declare_import(module, field, EntityType::Module(signature)); Ok(()) } @@ -455,12 +571,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data field: Option<&'data str>, ) -> WasmResult<()> { let signature = self.type_to_instance_type(ty_index)?; - let instance_index = self.result.module.instances.push(signature); - self.result.module.initializers.push(Initializer::Import { - module: module.to_owned(), - field: field.map(|s| s.to_owned()), - index: EntityIndex::Instance(instance_index), - }); + self.declare_import(module, field, EntityType::Instance(signature)); Ok(()) } @@ -755,62 +866,34 @@ and for re-adding support for interface types you can see this issue: self.result.module.initializers.reserve(amount as usize); } - fn declare_module(&mut self, ty: TypeIndex) -> WasmResult<()> { - // Record the type signature of this module ... - let signature = self.type_to_module_type(ty)?; - self.result.module.modules.push(signature); - - // ... and then record that in the initialization steps of this module - // we're inserting this module into the module index space. At this - // point we don't know the final index of the module we're defining, so - // we leave a placeholder to get rewritten later. - let loc = self.result.module.initializers.len(); - self.result - .module - .initializers - .push(Initializer::DefineModule(usize::max_value())); - self.result.module_initializer_indexes.push(loc); - Ok(()) - } - - fn module_start(&mut self, index: usize) { - // Reset the contents of `self.result` for a new module that's getting - // translataed. - let mut prev = mem::replace(&mut self.result, ModuleTranslation::default()); - - // If this is a nested submodule then we record the final destination of - // the child in parent (we store `index` into `prev`) in the appropriate - // initialization slot as dicated by `num_modules_defined` (our index of - // iteration through the code section). - // Record that the `num_modules_defined`-th module is defined at index - // by updating the initializer entry. - if index > 0 { - let initializer_idx = prev.module_initializer_indexes[prev.num_modules_defined]; - prev.num_modules_defined += 1; - debug_assert!(match &prev.module.initializers[initializer_idx] { - Initializer::DefineModule(usize::MAX) => true, - _ => false, - }); - prev.module.initializers[initializer_idx] = Initializer::DefineModule(index); - self.result.module.parent = Some(self.cur); + 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; } - - // Update our current index counter and save our parent's translation - // where this current translation will end up, which we'll swap back as - // part of `module_end`. - self.cur = index; - assert_eq!(index, self.results.len()); - self.results.push(prev); + // 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, index: usize) { - assert!(self.result.num_modules_defined == self.result.module_initializer_indexes.len()); - - // Move our finished module into its final location, swapping it with - // what was this module's parent. - self.cur = self.result.module.parent.unwrap_or(0); - mem::swap(&mut self.result, &mut self.results[index]); + fn module_end(&mut self) { + let (record_initializer, done) = match self.in_progress.pop() { + Some(m) => (true, mem::replace(&mut self.result, m)), + None => (false, mem::take(&mut self.result)), + }; + self.results.push(done); + if record_initializer { + let index = self.results.len() - 1; + self.result + .module + .initializers + .push(Initializer::DefineModule(index)); + let sig = self.gen_type_of_module(index); + self.result.module.modules.push(sig); + } } fn reserve_instances(&mut self, amt: u32) { @@ -818,7 +901,12 @@ and for re-adding support for interface types you can see this issue: self.result.module.initializers.reserve(amt as usize); } - fn declare_instance(&mut self, module: ModuleIndex, args: Vec) -> WasmResult<()> { + 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. @@ -839,29 +927,32 @@ and for re-adding support for interface types you can see this issue: // // Note that we don't add an initializer for this alias because // we statically know where all types point to. - Alias::ParentType(parent_idx) => { - let ty = self.results[self.cur].module.types[parent_idx]; + 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); } - // This is similar to types in that it's easy for us to record the - // type of the module that's being aliased, but we also need to add - // an initializer so during instantiation we can prepare the index - // space appropriately. - Alias::ParentModule(parent_idx) => { - let module_idx = self.results[self.cur].module.modules[parent_idx]; - self.result.module.modules.push(module_idx); - self.result - .module - .initializers - .push(Initializer::AliasParentModule(parent_idx)); + // FIXME(WebAssembly/module-linking#28) unsure how to implement this + // at this time, if we can alias imported modules it's a lot harder, + // otherwise we'll need to figure out how to translate `index` to a + // `usize` for a defined module (creating Initializer::DefineModule) + Alias::OuterModule { + relative_depth, + index, + } => { + drop((relative_depth, index)); + unimplemented!() } // 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::Child { instance, export } => { + Alias::InstanceExport { instance, export } => { let ty = self.result.module.instances[instance]; match &self.types.instance_signatures[ty].exports[export] { EntityType::Global(g) => { @@ -894,7 +985,10 @@ and for re-adding support for interface types you can see this issue: self.result .module .initializers - .push(Initializer::AliasInstanceExport { instance, export }) + .push(Initializer::AliasInstanceExport { + instance, + export: export.to_string(), + }) } } diff --git a/crates/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index 2cc157704a..17a0e811c7 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -13,12 +13,12 @@ arbitrary = { version = "0.4.1", features = ["derive"] } env_logger = "0.8.1" log = "0.4.8" rayon = "1.2.1" -wasmparser = "0.71" +wasmparser = "0.72" wasmprinter = "0.2.17" wasmtime = { path = "../wasmtime" } wasmtime-wast = { path = "../wast" } -wasm-encoder = "0.2" -wasm-smith = "0.3.0" +wasm-encoder = "0.4" +wasm-smith = "0.3.1" wasmi = "0.7.0" [dev-dependencies] diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml index bebb4d2841..b101c15819 100644 --- a/crates/jit/Cargo.toml +++ b/crates/jit/Cargo.toml @@ -28,7 +28,7 @@ rayon = { version = "1.0", optional = true } region = "2.1.0" thiserror = "1.0.4" target-lexicon = { version = "0.11.0", default-features = false } -wasmparser = "0.71" +wasmparser = "0.72" more-asserts = "0.2.1" anyhow = "1.0" cfg-if = "1.0" diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index f739b79d1a..c0738c9c2c 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -105,8 +105,8 @@ impl CompilationArtifacts { pub fn build( compiler: &Compiler, data: &[u8], - ) -> Result<(Vec, TypeTables), SetupError> { - let (translations, types) = ModuleEnvironment::new( + ) -> Result<(usize, Vec, TypeTables), SetupError> { + let (main_module, translations, types) = ModuleEnvironment::new( compiler.frontend_config(), compiler.tunables(), compiler.features(), @@ -166,6 +166,7 @@ impl CompilationArtifacts { }) .collect::, SetupError>>()?; Ok(( + main_module, list, TypeTables { wasm_signatures: types.wasm_signatures, diff --git a/crates/lightbeam/Cargo.toml b/crates/lightbeam/Cargo.toml index 89a18280fa..9b5632384d 100644 --- a/crates/lightbeam/Cargo.toml +++ b/crates/lightbeam/Cargo.toml @@ -24,7 +24,7 @@ more-asserts = "0.2.1" smallvec = "1.6.1" thiserror = "1.0.9" typemap = "0.3" -wasmparser = "0.71" +wasmparser = "0.72" [dev-dependencies] lazy_static = "1.2" diff --git a/crates/lightbeam/wasmtime/Cargo.toml b/crates/lightbeam/wasmtime/Cargo.toml index 71f43c8a3c..08ab2bac17 100644 --- a/crates/lightbeam/wasmtime/Cargo.toml +++ b/crates/lightbeam/wasmtime/Cargo.toml @@ -13,6 +13,6 @@ edition = "2018" [dependencies] lightbeam = { path = "..", version = "0.22.0" } -wasmparser = "0.71" +wasmparser = "0.72" cranelift-codegen = { path = "../../../cranelift/codegen", version = "0.69.0" } wasmtime-environ = { path = "../../environ", version = "0.22.0" } diff --git a/crates/runtime/src/export.rs b/crates/runtime/src/export.rs index 0161e56a68..b9564e571a 100644 --- a/crates/runtime/src/export.rs +++ b/crates/runtime/src/export.rs @@ -1,14 +1,14 @@ use crate::vmcontext::{ VMCallerCheckedAnyfunc, VMContext, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition, }; -use crate::InstanceHandle; +use crate::RuntimeInstance; use std::any::Any; use std::ptr::NonNull; use wasmtime_environ::wasm::Global; use wasmtime_environ::{MemoryPlan, TablePlan}; /// The value of an export passed from one instance to another. -pub enum Export<'a> { +pub enum Export { /// A function export value. Function(ExportFunction), @@ -22,10 +22,10 @@ pub enum Export<'a> { Global(ExportGlobal), /// An instance - Instance(&'a InstanceHandle), + Instance(RuntimeInstance), /// A module - Module(&'a dyn Any), + Module(Box), } /// A function export value. @@ -38,8 +38,8 @@ pub struct ExportFunction { pub anyfunc: NonNull, } -impl<'a> From for Export<'a> { - fn from(func: ExportFunction) -> Export<'a> { +impl From for Export { + fn from(func: ExportFunction) -> Export { Export::Function(func) } } @@ -55,8 +55,8 @@ pub struct ExportTable { pub table: TablePlan, } -impl<'a> From for Export<'a> { - fn from(func: ExportTable) -> Export<'a> { +impl From for Export { + fn from(func: ExportTable) -> Export { Export::Table(func) } } @@ -72,8 +72,8 @@ pub struct ExportMemory { pub memory: MemoryPlan, } -impl<'a> From for Export<'a> { - fn from(func: ExportMemory) -> Export<'a> { +impl From for Export { + fn from(func: ExportMemory) -> Export { Export::Memory(func) } } @@ -89,8 +89,8 @@ pub struct ExportGlobal { pub global: Global, } -impl<'a> From for Export<'a> { - fn from(func: ExportGlobal) -> Export<'a> { +impl From for Export { + fn from(func: ExportGlobal) -> Export { Export::Global(func) } } diff --git a/crates/runtime/src/imports.rs b/crates/runtime/src/imports.rs index 1969630750..fe6031293e 100644 --- a/crates/runtime/src/imports.rs +++ b/crates/runtime/src/imports.rs @@ -1,8 +1,4 @@ use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport}; -use crate::InstanceHandle; -use std::any::Any; -use wasmtime_environ::entity::PrimaryMap; -use wasmtime_environ::wasm::{InstanceIndex, ModuleIndex}; /// Resolved import pointers. /// @@ -28,15 +24,4 @@ pub struct Imports<'a> { /// Resolved addresses for imported globals. pub globals: &'a [VMGlobalImport], - - /// Resolved imported instances. - pub instances: PrimaryMap, - - /// Resolved imported modules. - /// - /// Note that `Box` here is chosen to allow the embedder of this crate - /// to pick an appropriate representation of what module type should be. For - /// example for the `wasmtime` crate it's `wasmtime::Module` but that's not - /// defined way down here in this low crate. - pub modules: PrimaryMap>, } diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index 9ff7c4ee6d..1df1cfb640 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -14,6 +14,7 @@ use crate::vmcontext::{ VMSharedSignatureIndex, VMTableDefinition, VMTableImport, }; use crate::{ExportFunction, ExportGlobal, ExportMemory, ExportTable}; +use indexmap::IndexMap; use memoffset::offset_of; use more_asserts::assert_lt; use std::alloc::{self, Layout}; @@ -22,17 +23,22 @@ use std::cell::RefCell; use std::collections::HashMap; use std::convert::TryFrom; use std::ptr::NonNull; +use std::rc::Rc; use std::sync::Arc; use std::{mem, ptr, slice}; use thiserror::Error; use wasmtime_environ::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap}; use wasmtime_environ::wasm::{ DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, - ElemIndex, EntityIndex, FuncIndex, GlobalIndex, GlobalInit, InstanceIndex, MemoryIndex, - ModuleIndex, SignatureIndex, TableElementType, TableIndex, WasmType, + ElemIndex, EntityIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex, + TableElementType, TableIndex, WasmType, }; use wasmtime_environ::{ir, DataInitializer, Module, ModuleType, TableElements, VMOffsets}; +/// Runtime representation of an instance value, which erases all `Instance` +/// information since instances are just a collection of values. +pub type RuntimeInstance = Rc>; + /// A WebAssembly instance. /// /// This is repr(C) to ensure that the vmctx field is last. @@ -50,15 +56,6 @@ pub(crate) struct Instance { /// WebAssembly table data. tables: BoxedSlice, - /// Instances our module defined and their handles. - instances: PrimaryMap, - - /// Modules that are located in our index space. - /// - /// For now these are `Box` so the caller can define the type of what a - /// module looks like. - modules: PrimaryMap>, - /// Passive elements in this instantiation. As `elem.drop`s happen, these /// entries get removed. A missing entry is considered equivalent to an /// empty slice. @@ -266,18 +263,8 @@ impl Instance { self.vmctx() as *const VMContext as *mut VMContext } - /// Lookup an export with the given name. - pub fn lookup(&self, field: &str) -> Option { - let export = if let Some(export) = self.module.exports.get(field) { - export.clone() - } else { - return None; - }; - Some(self.lookup_by_declaration(&export)) - } - /// Lookup an export with the given export declaration. - pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export<'_> { + pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export { match export { EntityIndex::Function(index) => { let anyfunc = self.get_caller_checked_anyfunc(*index).unwrap(); @@ -326,8 +313,9 @@ impl Instance { } .into(), - EntityIndex::Instance(index) => Export::Instance(&self.instances[*index]), - EntityIndex::Module(index) => Export::Module(&*self.modules[*index]), + EntityIndex::Instance(_) | EntityIndex::Module(_) => { + panic!("can't use this api for modules/instances") + } } } @@ -855,8 +843,6 @@ impl InstanceHandle { passive_elements: Default::default(), passive_data, host_state, - instances: imports.instances, - modules: imports.modules, vmctx: VMContext {}, }; let layout = instance.alloc_layout(); @@ -1015,11 +1001,6 @@ impl InstanceHandle { self.instance().module() } - /// Lookup an export with the given name. - pub fn lookup(&self, field: &str) -> Option { - self.instance().lookup(field) - } - /// Lookup an export with the given export declaration. pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export { self.instance().lookup_by_declaration(export) diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 97e3934ef0..00539c36b4 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -37,7 +37,7 @@ pub mod libcalls; pub use crate::export::*; pub use crate::externref::*; pub use crate::imports::Imports; -pub use crate::instance::{InstanceHandle, InstantiationError, LinkError}; +pub use crate::instance::{InstanceHandle, InstantiationError, LinkError, RuntimeInstance}; pub use crate::jit_int::GdbJitImageRegistration; pub use crate::memory::{RuntimeLinearMemory, RuntimeMemoryCreator}; pub use crate::mmap::Mmap; diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index 54da1d9873..5cb30d116f 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -16,7 +16,7 @@ wasmtime-jit = { path = "../jit", version = "0.22.0" } wasmtime-cache = { path = "../cache", version = "0.22.0", optional = true } wasmtime-profiling = { path = "../profiling", version = "0.22.0" } target-lexicon = { version = "0.11.0", default-features = false } -wasmparser = "0.71" +wasmparser = "0.72" anyhow = "1.0.19" region = "2.2.0" libc = "0.2" diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs index 9a075e308e..a3cede9cb7 100644 --- a/crates/wasmtime/src/externals.rs +++ b/crates/wasmtime/src/externals.rs @@ -112,26 +112,25 @@ impl Extern { } } - pub(crate) fn from_wasmtime_export( - wasmtime_export: wasmtime_runtime::Export, - instance: StoreInstanceHandle, + pub(crate) unsafe fn from_wasmtime_export( + wasmtime_export: &wasmtime_runtime::Export, + store: &Store, ) -> Extern { match wasmtime_export { wasmtime_runtime::Export::Function(f) => { - Extern::Func(Func::from_wasmtime_function(f, instance)) + Extern::Func(Func::from_wasmtime_function(f, store)) } wasmtime_runtime::Export::Memory(m) => { - Extern::Memory(Memory::from_wasmtime_memory(m, instance)) + Extern::Memory(Memory::from_wasmtime_memory(m, store)) } wasmtime_runtime::Export::Global(g) => { - Extern::Global(Global::from_wasmtime_global(g, instance)) + Extern::Global(Global::from_wasmtime_global(g, store)) } wasmtime_runtime::Export::Table(t) => { - Extern::Table(Table::from_wasmtime_table(t, instance)) + Extern::Table(Table::from_wasmtime_table(t, store)) } wasmtime_runtime::Export::Instance(i) => { - let handle = unsafe { instance.store.existing_instance_handle(i.clone()) }; - Extern::Instance(Instance::from_wasmtime(handle)) + Extern::Instance(Instance::from_wasmtime(i, store)) } wasmtime_runtime::Export::Module(m) => { Extern::Module(m.downcast_ref::().unwrap().clone()) @@ -335,13 +334,13 @@ impl Global { Ok(()) } - pub(crate) fn from_wasmtime_global( - wasmtime_export: wasmtime_runtime::ExportGlobal, - instance: StoreInstanceHandle, + pub(crate) unsafe fn from_wasmtime_global( + wasmtime_export: &wasmtime_runtime::ExportGlobal, + store: &Store, ) -> Global { Global { - instance, - wasmtime_export, + instance: store.existing_vmctx(wasmtime_export.vmctx), + wasmtime_export: wasmtime_export.clone(), } } @@ -354,6 +353,10 @@ impl Global { from: self.wasmtime_export.definition, } } + + pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportGlobal { + &self.wasmtime_export + } } /// A WebAssembly `table`, or an array of values. @@ -576,13 +579,13 @@ impl Table { Ok(()) } - pub(crate) fn from_wasmtime_table( - wasmtime_export: wasmtime_runtime::ExportTable, - instance: StoreInstanceHandle, + pub(crate) unsafe fn from_wasmtime_table( + wasmtime_export: &wasmtime_runtime::ExportTable, + store: &Store, ) -> Table { Table { - instance, - wasmtime_export, + instance: store.existing_vmctx(wasmtime_export.vmctx), + wasmtime_export: wasmtime_export.clone(), } } @@ -596,6 +599,10 @@ impl Table { vmctx: self.wasmtime_export.vmctx, } } + + pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportTable { + &self.wasmtime_export + } } /// A WebAssembly linear memory. @@ -987,13 +994,13 @@ impl Memory { .ok_or_else(|| anyhow!("failed to grow memory")) } - pub(crate) fn from_wasmtime_memory( - wasmtime_export: wasmtime_runtime::ExportMemory, - instance: StoreInstanceHandle, + pub(crate) unsafe fn from_wasmtime_memory( + wasmtime_export: &wasmtime_runtime::ExportMemory, + store: &Store, ) -> Memory { Memory { - instance, - wasmtime_export, + instance: store.existing_vmctx(wasmtime_export.vmctx), + wasmtime_export: wasmtime_export.clone(), } } @@ -1007,6 +1014,10 @@ impl Memory { vmctx: self.wasmtime_export.vmctx, } } + + pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportMemory { + &self.wasmtime_export + } } /// A linear memory. This trait provides an interface for raw memory buffers which are used diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 630c5f736d..23bf36f73b 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -1,6 +1,6 @@ use crate::store::StoreInner; use crate::trampoline::StoreInstanceHandle; -use crate::{Extern, ExternRef, FuncType, Memory, Store, Trap, Val, ValType}; +use crate::{Extern, ExternRef, FuncType, Store, Trap, Val, ValType}; use anyhow::{bail, ensure, Context as _, Result}; use smallvec::{smallvec, SmallVec}; use std::cmp::max; @@ -9,8 +9,9 @@ use std::mem; use std::panic::{self, AssertUnwindSafe}; use std::ptr::{self, NonNull}; use std::rc::Weak; +use wasmtime_environ::wasm::EntityIndex; use wasmtime_runtime::{ - raise_user_trap, Export, InstanceHandle, VMContext, VMFunctionBody, VMSharedSignatureIndex, + raise_user_trap, InstanceHandle, VMContext, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline, }; @@ -331,11 +332,8 @@ impl Func { debug_assert!( anyfunc.as_ref().type_index != wasmtime_runtime::VMSharedSignatureIndex::default() ); - - let instance_handle = wasmtime_runtime::InstanceHandle::from_vmctx(anyfunc.as_ref().vmctx); let export = wasmtime_runtime::ExportFunction { anyfunc }; - let instance = store.existing_instance_handle(instance_handle); - let f = Func::from_wasmtime_function(export, instance); + let f = Func::from_wasmtime_function(&export, store); Some(f) } @@ -649,24 +647,24 @@ impl Func { self.export.anyfunc } - pub(crate) fn from_wasmtime_function( - export: wasmtime_runtime::ExportFunction, - instance: StoreInstanceHandle, + pub(crate) unsafe fn from_wasmtime_function( + export: &wasmtime_runtime::ExportFunction, + store: &Store, ) -> Self { // Each function signature in a module should have a trampoline stored // on that module as well, so unwrap the result here since otherwise // it's a bug in wasmtime. - let trampoline = instance - .store + let anyfunc = export.anyfunc.as_ref(); + let trampoline = store .signatures() .borrow() - .lookup_shared(unsafe { export.anyfunc.as_ref().type_index }) + .lookup_shared(anyfunc.type_index) .expect("failed to retrieve trampoline from module") .1; Func { - instance, - export, + instance: store.existing_vmctx(anyfunc.vmctx), + export: export.clone(), trampoline, } } @@ -807,6 +805,10 @@ impl Func { } } } + + pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportFunction { + &self.export + } } impl fmt::Debug for Func { @@ -1506,10 +1508,16 @@ impl Caller<'_> { debug_assert!(self.store.upgrade().is_some()); let handle = Store::from_inner(self.store.upgrade()?).existing_instance_handle(instance); - let export = handle.lookup(name)?; - match export { - Export::Memory(m) => Some(Extern::Memory(Memory::from_wasmtime_memory(m, handle))), - Export::Function(f) => Some(Extern::Func(Func::from_wasmtime_function(f, handle))), + let index = handle.module().exports.get(name)?; + match index { + // Only allow memory/functions for now to emulate what interface + // types will once provide + EntityIndex::Memory(_) | EntityIndex::Function(_) => { + Some(Extern::from_wasmtime_export( + &handle.lookup_by_declaration(&index), + &handle.store, + )) + } _ => None, } } diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index 0dff161052..34f87e5c0a 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -1,21 +1,17 @@ -use crate::trampoline::StoreInstanceHandle; use crate::types::matching; use crate::{ - Engine, Export, Extern, ExternType, Func, Global, InstanceType, Memory, Module, Store, Table, - Trap, + Engine, Export, Extern, Func, Global, InstanceType, Memory, Module, Store, Table, Trap, }; use anyhow::{bail, Context, Error, Result}; use std::mem; -use std::sync::Arc; +use std::rc::Rc; use wasmtime_environ::entity::PrimaryMap; use wasmtime_environ::wasm::{ - EntityIndex, EntityType, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex, - TableIndex, + EntityIndex, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex, TableIndex, }; use wasmtime_environ::Initializer; -use wasmtime_jit::TypeTables; use wasmtime_runtime::{ - Imports, InstanceHandle, InstantiationError, StackMapRegistry, VMContext, + Imports, InstantiationError, RuntimeInstance, StackMapRegistry, VMContext, VMExternRefActivationsTable, VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport, }; @@ -36,8 +32,6 @@ use wasmtime_runtime::{ /// itself. /// * `type` - the type tables produced during compilation which /// `compiled_module`'s metadata references. -/// * `parent_modules` - this is the list of compiled modules the parent has. -/// This is only applicable on recursive instantiations. /// * `define_import` - this function, like the name implies, defines an import /// into the provided builder. The expected entity that it's defining is also /// passed in for the top-level case where type-checking is performed. This is @@ -45,9 +39,13 @@ use wasmtime_runtime::{ fn instantiate( store: &Store, module: &Module, - parent_modules: &PrimaryMap, - define_import: &mut dyn FnMut(&EntityIndex, &mut ImportsBuilder<'_>) -> Result<()>, -) -> Result { + define_import: &mut dyn FnMut( + &str, + Option<&str>, + &EntityIndex, + &mut ImportsBuilder<'_>, + ) -> Result<()>, +) -> Result { let compiled_module = module.compiled_module(); let env_module = compiled_module.module(); @@ -59,21 +57,9 @@ fn instantiate( // to fetching from the import list for the top-level module and // otherwise fetching from each nested instance's argument list for // submodules. - Initializer::Import { - index, - module, - field, - } => { - define_import(index, &mut imports).with_context(|| match field { - Some(name) => format!("incompatible import type for `{}::{}`", module, name), - None => format!("incompatible import type for `{}`", module), - })?; - } - - // This one's pretty easy, we're just picking up our parent's module - // and putting it into our own index space. - Initializer::AliasParentModule(idx) => { - imports.modules.push(parent_modules[*idx].clone()); + Initializer::Import { index, name, field } => { + define_import(name, field.as_deref(), index, &mut imports) + .with_context(|| format!("incompatible import type for `{}`", name))?; } // Turns out defining any kind of module is pretty easy, we're just @@ -95,18 +81,8 @@ fn instantiate( // handle comes from our same store, but this should be true because // we acquired the handle from an instance in the store. Initializer::AliasInstanceExport { instance, export } => { - let instance_ty = env_module.instances[*instance]; - let export_name = module.types().instance_signatures[instance_ty] - .exports - .get_index(*export) - .expect("validation bug - should be valid") - .0; - let handle = &imports.instances[*instance]; - let entity_index = &handle.module().exports[export_name]; - let item = Extern::from_wasmtime_export( - handle.lookup_by_declaration(entity_index), - unsafe { store.existing_instance_handle(handle.clone()) }, - ); + let export = &imports.instances[*instance][export]; + let item = unsafe { Extern::from_wasmtime_export(export, store) }; imports.push_extern(&item); } @@ -127,13 +103,13 @@ fn instantiate( // we're doing all of this in the context of our `Store` argument // above so we should be safe here. Initializer::Instantiate { module, args } => { - let mut args = args.iter(); let handle = instantiate( store, &imports.modules[*module], - &imports.modules, - &mut |_, builder| { - match *args.next().unwrap() { + &mut |name, field, _, builder| { + debug_assert!(field.is_none()); + let index = args.get(name).expect("should be present after validation"); + match *index { EntityIndex::Global(i) => { builder.globals.push(imports.globals[i]); } @@ -150,24 +126,17 @@ fn instantiate( builder.modules.push(imports.modules[i].clone()); } EntityIndex::Instance(i) => { - builder - .instances - .push(unsafe { imports.instances[i].clone() }); + builder.instances.push(imports.instances[i].clone()); } } Ok(()) }, )?; - imports.instances.push(unsafe { (*handle).clone() }); + imports.instances.push(handle); } } } - // With the above initialization done we've now acquired the final set of - // imports in all the right index spaces and everything. Time to carry on - // with the creation of our own instance. - let imports = imports.build(); - // Register the module just before instantiation to ensure we have a // trampoline registered for every signature and to preserve the module's // compiled JIT code within the `Store`. @@ -176,11 +145,11 @@ fn instantiate( let config = store.engine().config(); let instance = unsafe { let instance = compiled_module.instantiate( - imports, + imports.build(), &store.lookup_shared_signature(module.types()), config.memory_creator.as_ref().map(|a| a as _), store.interrupts(), - Box::new(module.types().clone()), + Box::new(()), store.externref_activations_table() as *const VMExternRefActivationsTable as *mut _, store.stack_map_registry() as *const StackMapRegistry as *mut _, )?; @@ -233,7 +202,29 @@ fn instantiate( } } - Ok(instance) + let exports = instance + .handle + .module() + .exports + .iter() + .map(|(name, index)| { + // Note that instances and modules are not handled by + // `wasmtime_runtime`, they're handled by us in this crate. That + // means we need to handle that here, otherwise we defer to the + // instance to load the values. + let item = match index { + EntityIndex::Instance(i) => { + wasmtime_runtime::Export::Instance(imports.instances[*i].clone()) + } + EntityIndex::Module(i) => { + wasmtime_runtime::Export::Module(Box::new(imports.modules[*i].clone())) + } + index => instance.handle.lookup_by_declaration(index), + }; + (name.clone(), item) + }) + .collect(); + Ok(Rc::new(exports)) } /// An instantiated WebAssembly module. @@ -254,7 +245,8 @@ fn instantiate( /// call any code or execute anything! #[derive(Clone)] pub struct Instance { - pub(crate) handle: StoreInstanceHandle, + pub(crate) store: Store, + pub(crate) items: RuntimeInstance, } impl Instance { @@ -316,7 +308,7 @@ impl Instance { bail!("cross-`Engine` instantiation is not currently supported"); } - // Perform some pre-flight checks before we get into the meat of + // Perform some pre-flight checks before we geet into the meat of // instantiation. let expected = module.compiled_module().module().imports().count(); if expected != imports.len() { @@ -329,32 +321,26 @@ impl Instance { } let mut imports = imports.iter(); - let handle = instantiate(store, module, &PrimaryMap::new(), &mut |idx, builder| { + let items = instantiate(store, module, &mut |_name, _field, idx, builder| { let import = imports.next().expect("already checked the length"); - builder.define_extern(idx, import) + builder.define_extern(idx, &import) })?; - Ok(Instance { handle }) + Ok(Instance::from_wasmtime(&items, store)) } - pub(crate) fn from_wasmtime(handle: StoreInstanceHandle) -> Instance { - Instance { handle } + pub(crate) fn from_wasmtime(handle: &RuntimeInstance, store: &Store) -> Instance { + Instance { + items: handle.clone(), + store: store.clone(), + } } /// Returns the type signature of this instance. pub fn ty(&self) -> InstanceType { let mut ty = InstanceType::new(); - let module = self.handle.module(); - let types = self - .handle - .host_state() - .downcast_ref::>() - .unwrap(); - for (name, index) in module.exports.iter() { - ty.add_named_export( - name, - ExternType::from_wasmtime(types, &module.type_of(*index)), - ); + for export in self.exports() { + ty.add_named_export(export.name(), export.ty()); } ty } @@ -364,16 +350,15 @@ impl Instance { /// This is the [`Store`] that generally serves as a sort of global cache /// for various instance-related things. pub fn store(&self) -> &Store { - &self.handle.store + &self.store } /// Returns the list of exported items from this [`Instance`]. pub fn exports<'instance>( &'instance self, ) -> impl ExactSizeIterator> + 'instance { - self.handle.exports().map(move |(name, entity_index)| { - let export = self.handle.lookup_by_declaration(entity_index); - let extern_ = Extern::from_wasmtime_export(export, self.handle.clone()); + self.items.iter().map(move |(name, item)| { + let extern_ = unsafe { Extern::from_wasmtime_export(item, &self.store) }; Export::new(name, extern_) }) } @@ -385,8 +370,8 @@ impl Instance { /// /// Returns `None` if there was no export named `name`. pub fn get_export(&self, name: &str) -> Option { - let export = self.handle.lookup(&name)?; - Some(Extern::from_wasmtime_export(export, self.handle.clone())) + let export = self.items.get(name)?; + Some(unsafe { Extern::from_wasmtime_export(export, &self.store) }) } /// Looks up an exported [`Func`] value by name. @@ -427,7 +412,7 @@ struct ImportsBuilder<'a> { tables: PrimaryMap, memories: PrimaryMap, globals: PrimaryMap, - instances: PrimaryMap, + instances: PrimaryMap, modules: PrimaryMap, module: &'a wasmtime_environ::Module, @@ -452,36 +437,7 @@ impl<'a> ImportsBuilder<'a> { fn define_extern(&mut self, expected: &EntityIndex, actual: &Extern) -> Result<()> { let expected_ty = self.module.type_of(*expected); - let compatible = match &expected_ty { - EntityType::Table(i) => match actual { - Extern::Table(e) => self.matcher.table(i, e), - _ => bail!("expected table, but found {}", actual.desc()), - }, - EntityType::Memory(i) => match actual { - Extern::Memory(e) => self.matcher.memory(i, e), - _ => bail!("expected memory, but found {}", actual.desc()), - }, - EntityType::Global(i) => match actual { - Extern::Global(e) => self.matcher.global(i, e), - _ => bail!("expected global, but found {}", actual.desc()), - }, - EntityType::Function(i) => match actual { - Extern::Func(e) => self.matcher.func(*i, e), - _ => bail!("expected func, but found {}", actual.desc()), - }, - EntityType::Instance(i) => match actual { - Extern::Instance(e) => self.matcher.instance(*i, e), - _ => bail!("expected instance, but found {}", actual.desc()), - }, - EntityType::Module(i) => match actual { - Extern::Module(e) => self.matcher.module(*i, e), - _ => bail!("expected module, but found {}", actual.desc()), - }, - EntityType::Event(_) => unimplemented!(), - }; - if !compatible { - bail!("{} types incompatible", actual.desc()); - } + self.matcher.extern_(&expected_ty, actual)?; self.push_extern(actual); Ok(()) } @@ -502,7 +458,7 @@ impl<'a> ImportsBuilder<'a> { } Extern::Instance(i) => { debug_assert!(Store::same(i.store(), self.matcher.store)); - self.instances.push(unsafe { (*i.handle).clone() }); + self.instances.push(i.items.clone()); } Extern::Module(m) => { self.modules.push(m.clone()); @@ -516,11 +472,40 @@ impl<'a> ImportsBuilder<'a> { globals: self.globals.values().as_slice(), memories: self.memories.values().as_slice(), functions: self.functions.values().as_slice(), - instances: mem::take(&mut self.instances), - modules: mem::take(&mut self.modules) - .into_iter() - .map(|(_, m)| Box::new(m) as Box<_>) - .collect(), + } + } +} + +/// An internal structure to this crate to build an `Instance` from a list of +/// items with names. This is intended to stay private for now, it'll need an +/// audit of APIs if publicly exported. +#[derive(Default)] +pub(crate) struct InstanceBuilder { + items: RuntimeInstance, +} + +impl InstanceBuilder { + pub(crate) fn new() -> InstanceBuilder { + InstanceBuilder::default() + } + + pub(crate) fn insert(&mut self, name: &str, item: impl Into) { + let items = Rc::get_mut(&mut self.items).unwrap(); + let export = match item.into() { + Extern::Func(i) => wasmtime_runtime::Export::Function(i.wasmtime_export().clone()), + Extern::Memory(i) => wasmtime_runtime::Export::Memory(i.wasmtime_export().clone()), + Extern::Table(i) => wasmtime_runtime::Export::Table(i.wasmtime_export().clone()), + Extern::Global(i) => wasmtime_runtime::Export::Global(i.wasmtime_export().clone()), + Extern::Instance(i) => wasmtime_runtime::Export::Instance(i.items.clone()), + Extern::Module(i) => wasmtime_runtime::Export::Module(Box::new(i.clone())), + }; + items.insert(name.to_string(), export); + } + + pub(crate) fn finish(self, store: &Store) -> Instance { + Instance { + store: store.clone(), + items: self.items, } } } diff --git a/crates/wasmtime/src/linker.rs b/crates/wasmtime/src/linker.rs index 763d0fa3cb..396327b5d5 100644 --- a/crates/wasmtime/src/linker.rs +++ b/crates/wasmtime/src/linker.rs @@ -1,3 +1,4 @@ +use crate::instance::InstanceBuilder; use crate::{ Extern, ExternType, Func, FuncType, GlobalType, ImportType, Instance, IntoFunc, Module, Store, Trap, @@ -654,7 +655,37 @@ impl Linker { }, kind: self.import_kind(import.ty()), }; - self.map.get(&key).cloned() + if let Some(result) = self.map.get(&key).cloned() { + return Some(result); + } + + // This is a key location where the module linking proposal is + // implemented. This logic allows single-level imports of an instance to + // get satisfied by multiple definitions of items within this `Linker`. + // + // The instance being import is iterated over to load the names from + // this `Linker` (recursively calling `get`). If anything isn't defined + // we return `None` since the entire value isn't defined. Otherwise when + // all values are loaded it's assembled into an `Instance` and + // returned`. + // + // Note that this isn't exactly the speediest implementation in the + // world. Ideally we would pre-create the `Instance` instead of creating + // it each time a module is instantiated. For now though while the + // module linking proposal is under development this should hopefully + // suffice. + if let ExternType::Instance(t) = import.ty() { + if import.name().is_none() { + let mut builder = InstanceBuilder::new(); + for export in t.exports() { + let item = self.get(&export.as_import(import.module()))?; + builder.insert(export.name(), item); + } + return Some(builder.finish(&self.store).into()); + } + } + + None } /// Returns all items defined for the `module` and `name` pair. diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs index d30309c5fc..c78ae8a7f2 100644 --- a/crates/wasmtime/src/module.rs +++ b/crates/wasmtime/src/module.rs @@ -245,12 +245,14 @@ impl Module { /// ``` pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result { #[cfg(feature = "cache")] - let (artifacts, types) = ModuleCacheEntry::new("wasmtime", engine.cache_config()) - .get_data((engine.compiler(), binary), |(compiler, binary)| { - CompilationArtifacts::build(compiler, binary) - })?; + let (main_module, artifacts, types) = + ModuleCacheEntry::new("wasmtime", engine.cache_config()) + .get_data((engine.compiler(), binary), |(compiler, binary)| { + CompilationArtifacts::build(compiler, binary) + })?; #[cfg(not(feature = "cache"))] - let (artifacts, types) = CompilationArtifacts::build(engine.compiler(), binary)?; + let (main_module, artifacts, types) = + CompilationArtifacts::build(engine.compiler(), binary)?; let modules = CompiledModule::from_artifacts_list( artifacts, @@ -261,7 +263,7 @@ impl Module { let types = Arc::new(types); Ok(Module { engine: engine.clone(), - index: 0, + index: main_module, data: Arc::new(ModuleData { types, modules }), }) } diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index 6b314f6ac1..9c622faadf 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -13,8 +13,8 @@ use std::sync::Arc; use wasmtime_environ::wasm; use wasmtime_jit::{CompiledModule, ModuleCode, TypeTables}; use wasmtime_runtime::{ - InstanceHandle, RuntimeMemoryCreator, SignalHandler, StackMapRegistry, TrapInfo, VMExternRef, - VMExternRefActivationsTable, VMInterrupts, VMSharedSignatureIndex, + InstanceHandle, RuntimeMemoryCreator, SignalHandler, StackMapRegistry, TrapInfo, VMContext, + VMExternRef, VMExternRefActivationsTable, VMInterrupts, VMSharedSignatureIndex, }; /// A `Store` is a collection of WebAssembly instances and host-defined items. @@ -234,6 +234,10 @@ impl Store { } } + pub(crate) unsafe fn existing_vmctx(&self, cx: *mut VMContext) -> StoreInstanceHandle { + self.existing_instance_handle(InstanceHandle::from_vmctx(cx)) + } + pub(crate) fn weak(&self) -> Weak { Rc::downgrade(&self.inner) } diff --git a/crates/wasmtime/src/trampoline/global.rs b/crates/wasmtime/src/trampoline/global.rs index 00a91bdb91..bb20bb1569 100644 --- a/crates/wasmtime/src/trampoline/global.rs +++ b/crates/wasmtime/src/trampoline/global.rs @@ -48,7 +48,7 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result Result unsafe { *(*g.definition).as_externref_mut() = Some(x.inner); }, diff --git a/crates/wasmtime/src/trampoline/mod.rs b/crates/wasmtime/src/trampoline/mod.rs index e8c836b2ec..4772d7481a 100644 --- a/crates/wasmtime/src/trampoline/mod.rs +++ b/crates/wasmtime/src/trampoline/mod.rs @@ -16,6 +16,7 @@ use crate::{FuncType, GlobalType, MemoryType, Store, TableType, Trap, Val}; use anyhow::Result; use std::any::Any; use std::ops::Deref; +use wasmtime_environ::wasm; use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody, VMTrampoline}; /// A wrapper around `wasmtime_runtime::InstanceHandle` which pairs it with the @@ -55,7 +56,8 @@ pub fn generate_func_export( VMTrampoline, )> { let (instance, trampoline) = create_handle_with_function(ft, func, store)?; - match instance.lookup("").expect("trampoline export") { + let idx = wasm::EntityIndex::Function(wasm::FuncIndex::from_u32(0)); + match instance.lookup_by_declaration(&idx) { wasmtime_runtime::Export::Function(f) => Ok((instance, f, trampoline)), _ => unreachable!(), } @@ -72,7 +74,8 @@ pub unsafe fn generate_raw_func_export( state: Box, ) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportFunction)> { let instance = func::create_handle_with_raw_function(ft, func, trampoline, store, state)?; - match instance.lookup("").expect("trampoline export") { + let idx = wasm::EntityIndex::Function(wasm::FuncIndex::from_u32(0)); + match instance.lookup_by_declaration(&idx) { wasmtime_runtime::Export::Function(f) => Ok((instance, f)), _ => unreachable!(), } @@ -84,7 +87,8 @@ pub fn generate_global_export( val: Val, ) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportGlobal)> { let instance = create_global(store, gt, val)?; - match instance.lookup("").expect("global export") { + let idx = wasm::EntityIndex::Global(wasm::GlobalIndex::from_u32(0)); + match instance.lookup_by_declaration(&idx) { wasmtime_runtime::Export::Global(g) => Ok((instance, g)), _ => unreachable!(), } @@ -95,7 +99,8 @@ pub fn generate_memory_export( m: &MemoryType, ) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportMemory)> { let instance = create_handle_with_memory(store, m)?; - match instance.lookup("").expect("memory export") { + let idx = wasm::EntityIndex::Memory(wasm::MemoryIndex::from_u32(0)); + match instance.lookup_by_declaration(&idx) { wasmtime_runtime::Export::Memory(m) => Ok((instance, m)), _ => unreachable!(), } @@ -106,7 +111,8 @@ pub fn generate_table_export( t: &TableType, ) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportTable)> { let instance = create_handle_with_table(store, t)?; - match instance.lookup("").expect("table export") { + let idx = wasm::EntityIndex::Table(wasm::TableIndex::from_u32(0)); + match instance.lookup_by_declaration(&idx) { wasmtime_runtime::Export::Table(t) => Ok((instance, t)), _ => unreachable!(), } diff --git a/crates/wasmtime/src/trampoline/table.rs b/crates/wasmtime/src/trampoline/table.rs index 7c451f5cb0..c151fca51d 100644 --- a/crates/wasmtime/src/trampoline/table.rs +++ b/crates/wasmtime/src/trampoline/table.rs @@ -23,6 +23,7 @@ pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result, ty: ExternType) { + pub(crate) fn add_named_import(&mut self, module: &str, field: Option<&str>, ty: ExternType) { self.imports .push((module.to_string(), field.map(|f| f.to_string()), ty)); } /// Returns the list of imports associated with this module type. pub fn imports(&self) -> impl ExactSizeIterator> { - self.imports.iter().map(|(module, name, ty)| ImportType { - module, - name: name.as_deref(), + self.imports.iter().map(|(name, field, ty)| ImportType { + module: name, + name: field.as_deref(), ty: EntityOrExtern::Extern(ty), }) } @@ -506,13 +506,7 @@ impl ModuleType { imports: ty .imports .iter() - .map(|(m, name, ty)| { - ( - m.to_string(), - name.as_ref().map(|n| n.to_string()), - ExternType::from_wasmtime(types, ty), - ) - }) + .map(|(m, ty)| (m.to_string(), None, ExternType::from_wasmtime(types, ty))) .collect(), } } @@ -615,8 +609,9 @@ impl<'module> ImportType<'module> { /// Returns the field name of the module that this import is expected to /// come from. /// - /// Note that the name can be `None` for the module linking proposal. If the - /// module linking proposal is not enabled it's safe to unwrap this. + /// Note that this is optional due to the module linking proposal. If the + /// module linking proposal is enabled this is always `None`, otherwise this + /// is always `Some`. pub fn name(&self) -> Option<&'module str> { self.name } @@ -683,6 +678,17 @@ impl<'module> ExportType<'module> { EntityOrExtern::Extern(e) => (*e).clone(), } } + + pub(crate) fn as_import<'a>(&self, module: &'a str) -> ImportType<'a> + where + 'module: 'a, + { + ImportType { + module, + name: Some(self.name), + ty: self.ty.clone(), + } + } } impl<'module> fmt::Debug for ExportType<'module> { diff --git a/crates/wasmtime/src/types/matching.rs b/crates/wasmtime/src/types/matching.rs index 018ab35fd9..a767bbb7e1 100644 --- a/crates/wasmtime/src/types/matching.rs +++ b/crates/wasmtime/src/types/matching.rs @@ -1,5 +1,5 @@ -use crate::Store; -use std::sync::Arc; +use crate::{Extern, Store}; +use anyhow::{bail, Context, Result}; use wasmtime_environ::wasm::{ EntityType, Global, InstanceTypeIndex, Memory, ModuleTypeIndex, SignatureIndex, Table, }; @@ -11,22 +11,27 @@ pub struct MatchCx<'a> { } impl MatchCx<'_> { - pub fn global(&self, expected: &Global, actual: &crate::Global) -> bool { + pub fn global(&self, expected: &Global, actual: &crate::Global) -> Result<()> { self.global_ty(expected, actual.wasmtime_ty()) } - fn global_ty(&self, expected: &Global, actual: &Global) -> bool { - expected.ty == actual.ty + fn global_ty(&self, expected: &Global, actual: &Global) -> Result<()> { + if expected.ty == actual.ty && expected.wasm_ty == actual.wasm_ty && expected.mutability == actual.mutability + { + Ok(()) + } else { + bail!("global types incompatible") + } } - pub fn table(&self, expected: &Table, actual: &crate::Table) -> bool { + pub fn table(&self, expected: &Table, actual: &crate::Table) -> Result<()> { self.table_ty(expected, actual.wasmtime_ty()) } - fn table_ty(&self, expected: &Table, actual: &Table) -> bool { - expected.wasm_ty == actual.wasm_ty + fn table_ty(&self, expected: &Table, actual: &Table) -> Result<()> { + if expected.wasm_ty == actual.wasm_ty && expected.ty == actual.ty && expected.minimum <= actual.minimum && match expected.maximum { @@ -36,14 +41,19 @@ impl MatchCx<'_> { }, None => true, } + { + Ok(()) + } else { + bail!("table types incompatible") + } } - pub fn memory(&self, expected: &Memory, actual: &crate::Memory) -> bool { + pub fn memory(&self, expected: &Memory, actual: &crate::Memory) -> Result<()> { self.memory_ty(expected, actual.wasmtime_ty()) } - fn memory_ty(&self, expected: &Memory, actual: &Memory) -> bool { - expected.shared == actual.shared + fn memory_ty(&self, expected: &Memory, actual: &Memory) -> Result<()> { + if expected.shared == actual.shared && expected.minimum <= actual.minimum && match expected.maximum { Some(expected) => match actual.maximum { @@ -52,10 +62,15 @@ impl MatchCx<'_> { }, None => true, } + { + Ok(()) + } else { + bail!("memory types incompatible") + } } - pub fn func(&self, expected: SignatureIndex, actual: &crate::Func) -> bool { - match self + pub fn func(&self, expected: SignatureIndex, actual: &crate::Func) -> Result<()> { + let matches = match self .store .signatures() .borrow() @@ -65,31 +80,50 @@ impl MatchCx<'_> { // If our expected signature isn't registered, then there's no way // that `actual` can match it. None => false, + }; + if matches { + Ok(()) + } else { + bail!("function types incompatible") } } - pub fn instance(&self, expected: InstanceTypeIndex, actual: &crate::Instance) -> bool { - let module = actual.handle.module(); - self.exports_match( - expected, - actual - .handle - .host_state() - .downcast_ref::>() - .unwrap(), - |name| module.exports.get(name).map(|idx| module.type_of(*idx)), - ) + pub fn instance(&self, expected: InstanceTypeIndex, actual: &crate::Instance) -> Result<()> { + for (name, expected) in self.types.instance_signatures[expected].exports.iter() { + match actual.items.get(name) { + Some(item) => { + let item = unsafe { Extern::from_wasmtime_export(item, self.store) }; + self.extern_(expected, &item) + .with_context(|| format!("instance export {:?} incompatible", name))?; + } + None => bail!("instance type missing export {:?}", name), + } + } + Ok(()) } /// Validates that the type signature of `actual` matches the `expected` /// module type signature. - pub fn module(&self, expected: ModuleTypeIndex, actual: &crate::Module) -> bool { + pub fn module(&self, expected: ModuleTypeIndex, actual: &crate::Module) -> Result<()> { + // This should only ever be invoked with module linking, and this is an + // early check that our `field` assertion below should always work as + // well. + assert!(self.store.engine().config().features.module_linking); + let expected_sig = &self.types.module_signatures[expected]; let module = actual.compiled_module().module(); - self.imports_match(expected, actual.types(), module.imports()) - && self.exports_match(expected_sig.exports, actual.types(), |name| { - module.exports.get(name).map(|idx| module.type_of(*idx)) - }) + self.imports_match( + expected, + actual.types(), + module.imports().map(|(name, field, ty)| { + assert!(field.is_none()); // should be true if module linking is enabled + (name, ty) + }), + )?; + self.exports_match(expected_sig.exports, actual.types(), |name| { + module.exports.get(name).map(|idx| module.type_of(*idx)) + })?; + Ok(()) } /// Validates that the `actual_imports` list of module imports matches the @@ -100,19 +134,25 @@ impl MatchCx<'_> { &self, expected: ModuleTypeIndex, actual_types: &TypeTables, - mut actual_imports: impl Iterator, EntityType)>, - ) -> bool { + actual_imports: impl Iterator, + ) -> Result<()> { + // Imports match if all of the actual imports are satisfied by the + // expected set of imports. Note that we're reversing the order of the + // subtytpe matching here too. let expected_sig = &self.types.module_signatures[expected]; - for (_, _, expected) in expected_sig.imports.iter() { - let (_, _, ty) = match actual_imports.next() { - Some(e) => e, - None => return false, + for (name, actual_ty) in actual_imports { + let expected_ty = match expected_sig.imports.get(name) { + Some(ty) => ty, + None => bail!("expected type doesn't import {:?}", name), }; - if !self.extern_ty_matches(expected, &ty, actual_types) { - return false; + MatchCx { + types: actual_types, + store: self.store, } + .extern_ty_matches(&actual_ty, expected_ty, self.types) + .with_context(|| format!("module import {:?} incompatible", name))?; } - actual_imports.next().is_none() + Ok(()) } /// Validates that all exports in `expected` are defined by `lookup` within @@ -122,16 +162,19 @@ impl MatchCx<'_> { expected: InstanceTypeIndex, actual_types: &TypeTables, lookup: impl Fn(&str) -> Option, - ) -> bool { + ) -> Result<()> { // The `expected` type must be a subset of `actual`, meaning that all // names in `expected` must be present in `actual`. Note that we do // name-based lookup here instead of index-based lookup. - self.types.instance_signatures[expected].exports.iter().all( - |(name, expected)| match lookup(name) { - Some(ty) => self.extern_ty_matches(expected, &ty, actual_types), - None => false, - }, - ) + for (name, expected) in self.types.instance_signatures[expected].exports.iter() { + match lookup(name) { + Some(ty) => self + .extern_ty_matches(expected, &ty, actual_types) + .with_context(|| format!("export {:?} incompatible", name))?, + None => bail!("failed to find export {:?}", name), + } + } + Ok(()) } /// Validates that the `expected` entity matches the `actual_ty` defined @@ -141,34 +184,49 @@ impl MatchCx<'_> { expected: &EntityType, actual_ty: &EntityType, actual_types: &TypeTables, - ) -> bool { + ) -> Result<()> { + let actual_desc = match actual_ty { + EntityType::Global(_) => "global", + EntityType::Module(_) => "module", + EntityType::Memory(_) => "memory", + EntityType::Event(_) => "event", + EntityType::Instance(_) => "instance", + EntityType::Table(_) => "table", + EntityType::Function(_) => "function", + }; match expected { EntityType::Global(expected) => match actual_ty { EntityType::Global(actual) => self.global_ty(expected, actual), - _ => false, + _ => bail!("expected global, but found {}", actual_desc), }, EntityType::Table(expected) => match actual_ty { EntityType::Table(actual) => self.table_ty(expected, actual), - _ => false, + _ => bail!("expected table, but found {}", actual_desc), }, EntityType::Memory(expected) => match actual_ty { EntityType::Memory(actual) => self.memory_ty(expected, actual), - _ => false, + _ => bail!("expected memory, but found {}", actual_desc), }, EntityType::Function(expected) => match *actual_ty { EntityType::Function(actual) => { - self.types.wasm_signatures[*expected] == actual_types.wasm_signatures[actual] + if self.types.wasm_signatures[*expected] == actual_types.wasm_signatures[actual] + { + Ok(()) + } else { + bail!("function types incompatible") + } } - _ => false, + _ => bail!("expected function, but found {}", actual_desc), }, EntityType::Instance(expected) => match actual_ty { EntityType::Instance(actual) => { let sig = &actual_types.instance_signatures[*actual]; self.exports_match(*expected, actual_types, |name| { sig.exports.get(name).cloned() - }) + })?; + Ok(()) } - _ => false, + _ => bail!("expected instance, but found {}", actual_desc), }, EntityType::Module(expected) => match actual_ty { EntityType::Module(actual) => { @@ -180,14 +238,48 @@ impl MatchCx<'_> { self.imports_match( *expected, actual_types, - actual_module_sig.imports.iter().map(|(module, field, ty)| { - (module.as_str(), field.as_deref(), ty.clone()) - }), - ) && self.exports_match(expected_module_sig.exports, actual_types, |name| { + actual_module_sig + .imports + .iter() + .map(|(module, ty)| (module.as_str(), ty.clone())), + )?; + self.exports_match(expected_module_sig.exports, actual_types, |name| { actual_instance_sig.exports.get(name).cloned() - }) + })?; + Ok(()) } - _ => false, + _ => bail!("expected module, but found {}", actual_desc), + }, + EntityType::Event(_) => unimplemented!(), + } + } + + /// Validates that the `expected` type matches the type of `actual` + pub fn extern_(&self, expected: &EntityType, actual: &Extern) -> Result<()> { + match expected { + EntityType::Global(expected) => match actual { + Extern::Global(actual) => self.global(expected, actual), + _ => bail!("expected global, but found {}", actual.desc()), + }, + EntityType::Table(expected) => match actual { + Extern::Table(actual) => self.table(expected, actual), + _ => bail!("expected table, but found {}", actual.desc()), + }, + EntityType::Memory(expected) => match actual { + Extern::Memory(actual) => self.memory(expected, actual), + _ => bail!("expected memory, but found {}", actual.desc()), + }, + EntityType::Function(expected) => match actual { + Extern::Func(actual) => self.func(*expected, actual), + _ => bail!("expected func, but found {}", actual.desc()), + }, + EntityType::Instance(expected) => match actual { + Extern::Instance(actual) => self.instance(*expected, actual), + _ => bail!("expected instance, but found {}", actual.desc()), + }, + EntityType::Module(expected) => match actual { + Extern::Module(actual) => self.module(*expected, actual), + _ => bail!("expected module, but found {}", actual.desc()), }, EntityType::Event(_) => unimplemented!(), } diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml index bbe1277f4c..97e660d0d1 100644 --- a/crates/wast/Cargo.toml +++ b/crates/wast/Cargo.toml @@ -13,7 +13,7 @@ edition = "2018" [dependencies] anyhow = "1.0.19" wasmtime = { path = "../wasmtime", version = "0.22.0", default-features = false } -wast = "29.0.0" +wast = "31.0.0" [badges] maintenance = { status = "actively-developed" } diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 73bd003e1e..df6905a23f 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -17,7 +17,7 @@ target-lexicon = "0.11" peepmatic-fuzzing = { path = "../cranelift/peepmatic/crates/fuzzing", optional = true } wasmtime = { path = "../crates/wasmtime" } wasmtime-fuzzing = { path = "../crates/fuzzing" } -wasm-smith = "0.3.0" +wasm-smith = "0.3.1" [features] experimental_x64 = ["wasmtime-fuzzing/experimental_x64"] diff --git a/fuzz/fuzz_targets/instantiate-swarm.rs b/fuzz/fuzz_targets/instantiate-swarm.rs index 092a32e925..d9c5bdb645 100644 --- a/fuzz/fuzz_targets/instantiate-swarm.rs +++ b/fuzz/fuzz_targets/instantiate-swarm.rs @@ -2,12 +2,13 @@ use libfuzzer_sys::fuzz_target; use std::time::Duration; -use wasm_smith::{ConfiguredModule, SwarmConfig}; +use wasm_smith::{Config, ConfiguredModule, SwarmConfig}; use wasmtime::Strategy; use wasmtime_fuzzing::oracles; fuzz_target!(|module: ConfiguredModule| { let mut cfg = wasmtime_fuzzing::fuzz_default_config(Strategy::Auto).unwrap(); cfg.wasm_multi_memory(true); + cfg.wasm_module_linking(module.config().module_linking_enabled()); oracles::instantiate_with_config(&module.to_bytes(), true, cfg, Some(Duration::from_secs(20))); }); diff --git a/src/obj.rs b/src/obj.rs index e957fb6afd..529d1c3c6d 100644 --- a/src/obj.rs +++ b/src/obj.rs @@ -65,7 +65,7 @@ pub fn compile_to_obj( ); let environ = ModuleEnvironment::new(compiler.isa().frontend_config(), &tunables, &features); - let (mut translation, types) = environ + let (_main_module, mut translation, types) = environ .translate(wasm) .context("failed to translate module")?; assert_eq!(translation.len(), 1); diff --git a/tests/all/module_linking.rs b/tests/all/module_linking.rs index f352eb7f54..10ccfc098d 100644 --- a/tests/all/module_linking.rs +++ b/tests/all/module_linking.rs @@ -105,8 +105,13 @@ fn imports_exports() -> Result<()> { assert_eq!(i.len(), 1); let import = i.next().unwrap(); assert_eq!(import.module(), ""); - assert_eq!(import.name(), Some("a")); - let module_ty = match import.ty() { + assert_eq!(import.name(), None); + let instance_ty = match import.ty() { + ExternType::Instance(t) => t, + _ => panic!("unexpected type"), + }; + assert_eq!(instance_ty.exports().len(), 1); + let module_ty = match instance_ty.exports().next().unwrap().ty() { ExternType::Module(m) => m, _ => panic!("unexpected type"), }; @@ -148,8 +153,13 @@ fn imports_exports() -> Result<()> { assert_eq!(i.len(), 1); let import = i.next().unwrap(); assert_eq!(import.module(), ""); - assert_eq!(import.name(), Some("b")); + assert_eq!(import.name(), None); let instance_ty = match import.ty() { + ExternType::Instance(t) => t, + _ => panic!("unexpected type"), + }; + assert_eq!(instance_ty.exports().len(), 1); + let instance_ty = match instance_ty.exports().next().unwrap().ty() { ExternType::Instance(m) => m, _ => panic!("unexpected type"), }; diff --git a/tests/misc_testsuite/module-linking/alias.wast b/tests/misc_testsuite/module-linking/alias.wast index 0c69b4084c..f9d2b2f3fd 100644 --- a/tests/misc_testsuite/module-linking/alias.wast +++ b/tests/misc_testsuite/module-linking/alias.wast @@ -7,7 +7,7 @@ (instance $a (instantiate $m)) (func (export "get") (result i32) - call $a.$foo) + call (func $a "foo")) ) (assert_return (invoke "get") (i32.const 1)) @@ -19,7 +19,7 @@ (instance $a (instantiate $m)) (func (export "get") (result i32) - global.get $a.$g) + global.get (global $a "g")) ) (assert_return (invoke "get") (i32.const 2)) @@ -30,7 +30,7 @@ (data (i32.const 0) "\03\00\00\00") ) (instance $a (instantiate $m)) - (alias (instance $a) (memory $m)) + (alias $m (memory $a "m")) (func (export "get") (result i32) i32.const 0 @@ -50,7 +50,7 @@ (func (export "get") (result i32) i32.const 0 - call_indirect $a.$t (result i32)) + call_indirect (table $a "t") (result i32)) ) (assert_return (invoke "get") (i32.const 4)) @@ -62,11 +62,10 @@ i32.const 5)) ) (instance $a (instantiate $m)) - (instance $b (instantiate $a.$sub)) - (alias $b.$f (instance $b) (func 0)) + (instance $b (instantiate (module $a "module"))) (func (export "get") (result i32) - call $b.$f) + call (func $b "")) ) (assert_return (invoke "get") (i32.const 5)) @@ -79,11 +78,9 @@ (instance $i (export "") (instantiate $sub)) ) (instance $a (instantiate $m)) - (alias $a.$i (instance $a) (instance 0)) - (alias $a.$i.$f (instance $a.$i) (func 0)) (func (export "get") (result i32) - call $a.$i.$f) + call (func $a "" "")) ) (assert_return (invoke "get") (i32.const 6)) @@ -91,48 +88,51 @@ (module (type $t (func)) (module $m - (func $f (type $t)) + (func $f (type outer 0 $t)) ) (instance $a (instantiate $m)) ) ;; alias parent -- module +(; TODO (module (module $a) (module $m - (instance (instantiate $a)) + (instance (instantiate (module outer 0 $a))) ) (instance (instantiate $m)) ) +;) ;; The alias, import, type, module, and instance sections can all be interleaved -(module +(module $ROOT (module $a) (type $t (func)) (module $m ;; alias - (alias $thunk parent (type $t)) + (alias $thunk (type outer 0 $t)) ;; import (import "" "" (func (type $thunk))) ;; module (referencing parent type) (module - (func (type $thunk)) + (func (type outer $m $thunk)) + (func (type outer $ROOT $t)) ) ;; type (type $thunk2 (func)) ;; module (referencing previous alias) (module $m2 - (func (export "") (type $thunk2)) + (func (export "") (type outer $m $thunk2)) ) ;; instance (instance $i (instantiate $m2)) ;; alias that instance - (alias $my_f (instance $i) (func 0)) + (alias $my_f (func $i "")) ;; module (module $m3 (import "" (func))) ;; use our aliased function to create the module - (instance $i2 (instantiate $m3 (func $my_f))) + (instance $i2 (instantiate $m3 "" (func $my_f))) ;; module (module $m4 (import "" (func))) @@ -141,5 +141,5 @@ ;; instantiate the above module (module $smol (func $f (export ""))) (instance $smol (instantiate $smol)) - (instance (instantiate $m (func $smol.$f))) + (instance (instantiate $m "" (instance $smol))) ) diff --git a/tests/misc_testsuite/module-linking/import-subtyping.wast b/tests/misc_testsuite/module-linking/import-subtyping.wast index 4ac1658070..2055f848f3 100644 --- a/tests/misc_testsuite/module-linking/import-subtyping.wast +++ b/tests/misc_testsuite/module-linking/import-subtyping.wast @@ -9,22 +9,36 @@ (module (import "a" "m" (module)) +) +(module (import "a" "m" (module (export "" (func)))) +) +(module (import "a" "m" (module (export "a" (func)))) +) +(module (import "a" "m" (module (export "b" (global i32)))) +) +(module (import "a" "m" (module (export "" (func)) (export "a" (func)) )) +) +(module (import "a" "m" (module (export "a" (func)) (export "" (func)) )) +) +(module (import "a" "m" (module (export "a" (func)) (export "" (func)) (export "b" (global i32)) )) +) +(module (import "a" "m" (module (export "b" (global i32)) (export "a" (func)) @@ -37,64 +51,60 @@ (module (export "m") (func (export "")))) -(module - (import "a" "m" (module)) - (import "a" "m" (module (export "" (func)))) -) +(module (import "a" "m" (module))) +(module (import "a" "m" (module (export "" (func))))) (assert_unlinkable (module (import "a" "m" (module (export "" (func (param i32)))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (func (result i32)))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (global i32))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (table 1 funcref))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (memory 1))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (module))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (instance))))) - "module types incompatible") + "incompatible import type for `a`") (module $a (module (export "m") (global (export "") i32 (i32.const 0)))) ;; globals -(module - (import "a" "m" (module)) - (import "a" "m" (module (export "" (global i32)))) -) +(module (import "a" "m" (module))) +(module (import "a" "m" (module (export "" (global i32))))) (assert_unlinkable (module (import "a" "m" (module (export "" (global (mut i32))))) ) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (global f32))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (func))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (table 1 funcref))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (memory 1))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (module))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (instance))))) - "module types incompatible") + "incompatible import type for `a`") ;; tables (module $a @@ -105,40 +115,52 @@ ) (module (import "a" "m" (module)) +) +(module (import "a" "m" (module (export "" (table 1 funcref)))) +) +(module (import "a" "m" (module (export "" (table 0 funcref)))) +) +(module (import "a" "m" (module (export "max" (table 1 10 funcref)))) +) +(module (import "a" "m" (module (export "max" (table 0 10 funcref)))) +) +(module (import "a" "m" (module (export "max" (table 0 11 funcref)))) +) +(module (import "a" "m" (module (export "max" (table 0 funcref)))) ) (assert_unlinkable (module (import "a" "m" (module (export "" (global f32))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (func))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (table 2 funcref))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (table 1 10 funcref))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "max" (table 2 10 funcref))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "max" (table 1 9 funcref))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (memory 1))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (module))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (instance))))) - "module types incompatible") + "incompatible import type for `a`") ;; memories (module $a @@ -149,40 +171,52 @@ ) (module (import "a" "m" (module)) +) +(module (import "a" "m" (module (export "" (memory 1)))) +) +(module (import "a" "m" (module (export "" (memory 0)))) +) +(module (import "a" "m" (module (export "max" (memory 1 10)))) +) +(module (import "a" "m" (module (export "max" (memory 0 10)))) +) +(module (import "a" "m" (module (export "max" (memory 0 11)))) +) +(module (import "a" "m" (module (export "max" (memory 0)))) ) (assert_unlinkable (module (import "a" "m" (module (export "" (global f32))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (func))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (table 1 funcref))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (memory 2))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (memory 1 10))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "max" (memory 2 10))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "max" (memory 2))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (module))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (instance))))) - "module types incompatible") + "incompatible import type for `a`") ;; modules (module $a @@ -206,72 +240,102 @@ ) ;; import a mixture (module (export "e") - (import "" (func)) - (import "" (func)) - (import "" (global i32)) + (import "a" (func)) + (import "b" (func)) + (import "c" (global i32)) ) ) ) (module (import "a" "m" (module)) +) +(module (import "a" "m" (module (export "a" (module)))) +) +(module (import "a" "m" (module (export "b" (module)))) +) +(module (import "a" "m" (module (export "b" (module (export "" (func)))))) +) +(module (import "a" "m" (module (export "c" (module)))) +) +(module (import "a" "m" (module (export "c" (module (export "a" (func)) )))) +) +(module (import "a" "m" (module (export "c" (module (export "a" (func)) (export "b" (func (result i32))) )))) +) +(module (import "a" "m" (module (export "c" (module (export "c" (global i32)) )))) +) +(module (import "a" "m" (module (export "c" (module (export "c" (global i32)) (export "a" (func)) )))) - - ;; for now import strings aren't matched at all, imports must simply pairwise - ;; line up - (import "a" "m" (module (export "d" (module (import "" (func)))))) - (import "a" "m" (module (export "d" (module (import "x" (func)))))) - (import "a" "m" (module (export "d" (module (import "x" "y" (func)))))) - - (import "a" "m" (module (export "e" (module - (import "x" "y" (func)) +) +(module + (import "a" "m" (module (export "d" (module + (import "" (func)) (import "a" (func)) - (import "z" (global i32)) + )))) +) +(module + (import "a" "m" (module (export "d" (module (import "" (func)))))) +) +(assert_unlinkable + (module + (import "a" "m" (module (export "d" (module (import "x" (func)))))) + ) + "incompatible import type for `a`") +(assert_unlinkable + (module + (import "a" "m" (module (export "d" (module (import "x" "y" (func)))))) + ) + "incompatible import type for `a`") +(module + (import "a" "m" (module (export "e" (module + (import "a" (func)) + (import "b" (func)) + (import "c" (global i32)) )))) ) (assert_unlinkable (module (import "a" "m" (module (export "" (module (export "a" (func))))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "d" (module))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "d" (module (import "" (module))))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (global f32))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (func))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (table 1 funcref))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (memory 2))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (module (export "foo" (func))))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (instance))))) - "module types incompatible") + "incompatible import type for `a`") ;; instances (module $a @@ -303,46 +367,65 @@ ) (module (import "a" "a" (instance)) +) +(module (import "a" "b" (instance)) +) +(module (import "a" "b" (instance (export "" (func)))) +) +(module (import "a" "c" (instance)) +) +(module (import "a" "c" (instance (export "a" (func)))) +) +(module (import "a" "c" (instance (export "b" (func (result i32))))) +) +(module (import "a" "c" (instance (export "c" (global i32)))) +) +(module (import "a" "c" (instance (export "a" (func)) (export "b" (func (result i32))) (export "c" (global i32)) )) +) +(module (import "a" "c" (instance (export "c" (global i32)) (export "a" (func)) )) - +) +(module (import "a" "m" (module (export "i" (instance)))) +) +(module (import "a" "m" (module (export "i" (instance (export "" (func)))))) ) (assert_unlinkable (module (import "a" "a" (instance (export "" (global f32))))) - "instance types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "i" (instance (export "x" (func))))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (func))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (table 1 funcref))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (memory 2))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (memory 1 10))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "max" (memory 2 10))))) - "module types incompatible") + "incompatible import type for `a`") (assert_unlinkable (module (import "a" "m" (module (export "" (module))))) - "module types incompatible") + "incompatible import type for `a`") diff --git a/tests/misc_testsuite/module-linking/instantiate.wast b/tests/misc_testsuite/module-linking/instantiate.wast index 8a61684438..257aeb90dc 100644 --- a/tests/misc_testsuite/module-linking/instantiate.wast +++ b/tests/misc_testsuite/module-linking/instantiate.wast @@ -34,7 +34,7 @@ (module (import "" (func)) (start 0)) - (instance $a (instantiate 0 (func $set))) + (instance $a (instantiate 0 "" (func $set))) ) (assert_return (invoke $a "get") (i32.const 1)) @@ -49,7 +49,7 @@ global.set 0) (start 0)) - (instance $a (instantiate 0 (global $g))) + (instance $a (instantiate 0 "" (global $g))) ) (assert_return (invoke $a "get") (i32.const 2)) @@ -63,7 +63,7 @@ call_indirect) (start 0)) - (instance $a (instantiate 0 (table $t))) + (instance $a (instantiate 0 "" (table $t))) ) (assert_return (invoke $a "get") (i32.const 3)) @@ -78,7 +78,7 @@ i32.store) (start 0)) - (instance $a (instantiate 0 (memory $m))) + (instance $a (instantiate 0 "" (memory $m))) ) (assert_return (invoke $a "load") (i32.const 100)) @@ -88,13 +88,13 @@ (module $m1 (import "" (instance (export "" (func)))) - (alias (instance 0) (func 0)) + (alias (func 0 "")) (start 0)) (module $m2 (func (export "") (import ""))) - (instance $i (instantiate $m2 (func $set))) - (instance (instantiate $m1 (instance $i))) + (instance $i (instantiate $m2 "" (func $set))) + (instance (instantiate $m1 "" (instance $i))) ) (assert_return (invoke $a "get") (i32.const 4)) @@ -106,14 +106,14 @@ (import "" (module $m (export "" (func $f (result i32))))) (instance $i (instantiate $m)) (func $get (export "") (result i32) - call $i.$f)) + call (func $i ""))) (module $m2 (func (export "") (result i32) i32.const 5)) - (instance $i (instantiate $m1 (module $m2))) + (instance $i (instantiate $m1 "" (module $m2))) (func (export "get") (result i32) - call $i.$get) + call (func $i "")) ) (assert_return (invoke "get") (i32.const 5)) @@ -122,16 +122,16 @@ (module $m (import "" (module $m (export "get" (func (result i32))))) (instance $i (instantiate $m)) - (alias $f (instance $i) (func 0)) + (alias $f (func $i "get")) (export "" (func $f)) ) (module $m2 (func (export "get") (result i32) i32.const 6)) - (instance $a (instantiate $m (module $m2))) + (instance $a (instantiate $m "" (module $m2))) (func (export "get") (result i32) - call $a.$f) + call (func $a "")) ) (assert_return (invoke "get") (i32.const 6)) @@ -143,10 +143,10 @@ (import "a" "memory" (memory $m 1)) (module - (import "" (memory 1)) - (import "" (global (mut i32))) - (import "" (table 1 funcref)) - (import "" (func)) + (import "m" (memory 1)) + (import "g" (global (mut i32))) + (import "t" (table 1 funcref)) + (import "f" (func)) (func $start call 0 @@ -163,10 +163,10 @@ (instance $a (instantiate 0 - (memory $m) - (global $g) - (table $t) - (func $f) + "m" (memory $m) + "g" (global $g) + "t" (table $t) + "f" (func $f) ) ) ) @@ -183,10 +183,10 @@ (module $mt (import "" (table 1 funcref))) (module $mg (import "" (global (mut i32)))) - (instance (instantiate $mm (memory $m))) - (instance (instantiate $mf (func $f))) - (instance (instantiate $mt (table $t))) - (instance (instantiate $mg (global $g))) + (instance (instantiate $mm "" (memory $m))) + (instance (instantiate $mf "" (func $f))) + (instance (instantiate $mt "" (table $t))) + (instance (instantiate $mg "" (global $g))) ) ;; instantiate nested @@ -204,13 +204,13 @@ (import "" (func)) (start 0) ) - (instance (instantiate 0 (func 0))) + (instance (instantiate 0 "" (func 0))) ) - (instance (instantiate 0 (func 0))) + (instance (instantiate 0 "" (func 0))) ) - (instance (instantiate 0 (func 0))) + (instance (instantiate 0 "" (func 0))) ) - (instance (instantiate 0 (func 0))) + (instance (instantiate 0 "" (func 0))) ) (assert_return (invoke $a "get") (i32.const 1)) @@ -219,20 +219,14 @@ (module (export "m")) (instance (export "i") (instantiate 0)) ) -(module - (import "b" "m" (module)) - (import "b" "i" (instance)) -) -(assert_unlinkable - (module - (import "b" "m" (module (import "" (func)))) - ) - "module types incompatible") +(module (import "b" "m" (module))) +(module (import "b" "m" (module (import "" (func))))) +(module (import "b" "i" (instance))) (assert_unlinkable (module (import "b" "i" (instance (export "" (func)))) ) - "instance types incompatible") + "incompatible import type") ;; ensure we ignore other exported items (module $b @@ -250,7 +244,7 @@ )) (func (export "get") (result i32) - global.get $i.$g) + global.get (global $i "g")) ) (assert_return (invoke "get") (i32.const 0xfeed)) @@ -270,15 +264,45 @@ (module (import "b" "i" (instance $i ;; notice that this order is swapped - (export "g" (func $g (param i32) (result i32))) - (export "f" (func $f (result i32))) + (export "g" (func (param i32) (result i32))) + (export "f" (func (result i32))) )) (func (export "f") (result i32) - call $i.$f) + call (func $i "f")) (func (export "g") (param i32) (result i32) local.get 0 - call $i.$g) + call (func $i "g")) ) (assert_return (invoke "f") (i32.const 300)) (assert_return (invoke "g" (i32.const 3000)) (i32.const 3100)) + +(module $a + (func (export "f"))) + +(module + (import "a" "f" (func)) + + (module $m1 + (import "a" "f" (func))) + (instance (instantiate $m1 "a" (instance 0))) +) + +(module + (import "a" "f" (func)) + + ;; this module provides nothing + (module $m1) + + ;; this module imports a module which it says imports something + (module $m2 + (module $a + (func (export ""))) + (instance $i (instantiate $a)) + (import "m" (module $b (import "" (func)))) + (instance $b (instantiate $b "" (func $i "")))) + + ;; we should be able to instantiate m2 with m1 because m1 doesn't actually + ;; import anything (always safe to remove imports!) + (instance (instantiate $m2 "m" (module $m1))) +)