Browse Source

Implement imported/exported modules/instances (#2461)

* Implement imported/exported modules/instances

This commit implements the final piece of the module linking proposal
which is to flesh out the support for importing/exporting instances and
modules. This ended up having a few changes:

* Two more `PrimaryMap` instances are now stored in an `Instance`. The value
  for instances is `InstanceHandle` (pretty easy) and for modules it's
  `Box<dyn Any>` (less easy).

* The custom host state for `InstanceHandle` for `wasmtime` is now
  `Arc<TypeTables` to be able to fully reconstruct an instance's types
  just from its instance.

* Type matching for imports now has been updated to take
  instances/modules into account.

One of the main downsides of this implementation is that type matching
of imports is duplicated between wasmparser and wasmtime, leading to
posssible bugs especially in the subtelties of module linking. I'm not
sure how best to unify these two pieces of validation, however, and it
may be more trouble than it's worth.

cc #2094

* Update wat/wast/wasmparser

* Review comments

* Fix a bug in publish script to vendor the right witx

Currently there's two witx binaries in our repository given the two wasi
spec submodules, so this updates the publication script to vendor the
right one.
pull/2471/head
Alex Crichton 4 years ago
committed by GitHub
parent
commit
f003388ec7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 53
      Cargo.lock
  2. 4
      Cargo.toml
  3. 2
      cranelift/codegen/Cargo.toml
  4. 2
      cranelift/peepmatic/Cargo.toml
  5. 2
      cranelift/peepmatic/crates/fuzzing/Cargo.toml
  6. 2
      cranelift/peepmatic/crates/runtime/Cargo.toml
  7. 2
      cranelift/peepmatic/crates/souper/Cargo.toml
  8. 2
      cranelift/peepmatic/crates/test-operator/Cargo.toml
  9. 2
      cranelift/wasm/Cargo.toml
  10. 2
      cranelift/wasm/src/translation_utils.rs
  11. 4
      crates/c-api/src/extern.rs
  12. 2
      crates/debug/Cargo.toml
  13. 2
      crates/environ/Cargo.toml
  14. 27
      crates/environ/src/module.rs
  15. 2
      crates/environ/src/module_environ.rs
  16. 4
      crates/fuzzing/Cargo.toml
  17. 2
      crates/jit/Cargo.toml
  18. 2
      crates/lightbeam/Cargo.toml
  19. 2
      crates/lightbeam/wasmtime/Cargo.toml
  20. 27
      crates/runtime/src/export.rs
  21. 24
      crates/runtime/src/imports.rs
  22. 22
      crates/runtime/src/instance.rs
  23. 3
      crates/wasmtime/Cargo.toml
  24. 91
      crates/wasmtime/src/externals.rs
  25. 6
      crates/wasmtime/src/func.rs
  26. 237
      crates/wasmtime/src/instance.rs
  27. 8
      crates/wasmtime/src/linker.rs
  28. 63
      crates/wasmtime/src/module.rs
  29. 18
      crates/wasmtime/src/store.rs
  30. 117
      crates/wasmtime/src/types.rs
  31. 195
      crates/wasmtime/src/types/matching.rs
  32. 2
      crates/wast/Cargo.toml
  33. 5
      scripts/publish.rs
  34. 4
      tests/all/wast.rs
  35. 33
      tests/misc_testsuite/module-linking/alias.wast
  36. 348
      tests/misc_testsuite/module-linking/import-subtyping.wast
  37. 38
      tests/misc_testsuite/module-linking/instantiate.wast

53
Cargo.lock

@ -394,7 +394,7 @@ dependencies = [
"souper-ir",
"target-lexicon",
"thiserror",
"wast 27.0.0",
"wast 28.0.0",
]
[[package]]
@ -596,7 +596,7 @@ dependencies = [
"smallvec",
"target-lexicon",
"thiserror",
"wasmparser 0.68.0",
"wasmparser 0.69.1",
"wat",
]
@ -1166,7 +1166,7 @@ dependencies = [
"smallvec",
"thiserror",
"typemap",
"wasmparser 0.68.0",
"wasmparser 0.69.1",
"wat",
]
@ -1410,7 +1410,7 @@ dependencies = [
"peepmatic-test-operator",
"peepmatic-traits",
"serde",
"wast 27.0.0",
"wast 28.0.0",
"z3",
]
@ -1438,7 +1438,7 @@ dependencies = [
"peepmatic-traits",
"rand",
"serde",
"wast 27.0.0",
"wast 28.0.0",
]
[[package]]
@ -1463,7 +1463,7 @@ dependencies = [
"serde",
"serde_test",
"thiserror",
"wast 27.0.0",
"wast 28.0.0",
]
[[package]]
@ -1475,7 +1475,7 @@ dependencies = [
"peepmatic",
"peepmatic-test-operator",
"souper-ir",
"wast 27.0.0",
"wast 28.0.0",
]
[[package]]
@ -1496,7 +1496,7 @@ version = "0.68.0"
dependencies = [
"peepmatic-traits",
"serde",
"wast 27.0.0",
"wast 28.0.0",
]
[[package]]
@ -2405,18 +2405,18 @@ checksum = "32fddd575d477c6e9702484139cf9f23dcd554b06d185ed0f56c857dd3a47aa6"
[[package]]
name = "wasmparser"
version = "0.68.0"
version = "0.69.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29a00e14eed9c2ecbbdbdd4fb284f49d21b6808965de24769a6379a13ec47d4c"
checksum = "fd19c6066bcf391a9d6f81db9b809f31d31723da2652c8416cb81cd5aabed944"
[[package]]
name = "wasmprinter"
version = "0.2.15"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f39a73b5f09cfcb1b568b61968d39b19e4ddec9b49040cfc091adf3b0788bca6"
checksum = "dba006f5c5bf41a2a5c3b45e861ea6eb067382acb022b6a35a00a0390f9547f6"
dependencies = [
"anyhow",
"wasmparser 0.68.0",
"wasmparser 0.69.1",
]
[[package]]
@ -2428,6 +2428,7 @@ dependencies = [
"bincode",
"cfg-if 1.0.0",
"cpp_demangle",
"indexmap",
"libc",
"log",
"region",
@ -2436,7 +2437,7 @@ dependencies = [
"smallvec",
"target-lexicon",
"tempfile",
"wasmparser 0.68.0",
"wasmparser 0.69.1",
"wasmtime-cache",
"wasmtime-environ",
"wasmtime-jit",
@ -2514,7 +2515,7 @@ dependencies = [
"test-programs",
"tracing-subscriber",
"wasi-common",
"wasmparser 0.68.0",
"wasmparser 0.69.1",
"wasmtime",
"wasmtime-cache",
"wasmtime-debug",
@ -2550,7 +2551,7 @@ dependencies = [
"object",
"target-lexicon",
"thiserror",
"wasmparser 0.68.0",
"wasmparser 0.69.1",
"wasmtime-environ",
]
@ -2569,7 +2570,7 @@ dependencies = [
"more-asserts",
"serde",
"thiserror",
"wasmparser 0.68.0",
"wasmparser 0.69.1",
]
[[package]]
@ -2598,7 +2599,7 @@ dependencies = [
"rayon",
"wasm-smith",
"wasmi",
"wasmparser 0.68.0",
"wasmparser 0.69.1",
"wasmprinter",
"wasmtime",
"wasmtime-wast",
@ -2626,7 +2627,7 @@ dependencies = [
"serde",
"target-lexicon",
"thiserror",
"wasmparser 0.68.0",
"wasmparser 0.69.1",
"wasmtime-cranelift",
"wasmtime-debug",
"wasmtime-environ",
@ -2643,7 +2644,7 @@ version = "0.21.0"
dependencies = [
"cranelift-codegen",
"lightbeam",
"wasmparser 0.68.0",
"wasmparser 0.69.1",
"wasmtime-environ",
]
@ -2750,7 +2751,7 @@ version = "0.21.0"
dependencies = [
"anyhow",
"wasmtime",
"wast 27.0.0",
"wast 28.0.0",
]
[[package]]
@ -2786,20 +2787,20 @@ dependencies = [
[[package]]
name = "wast"
version = "27.0.0"
version = "28.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2c3ef5f6a72dffa44c24d5811123f704e18a1dbc83637d347b1852b41d3835c"
checksum = "9c0586061bfacc035034672c8d760802b428ab4c80a92e2a392425c516df9be1"
dependencies = [
"leb128",
]
[[package]]
name = "wat"
version = "1.0.28"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835cf59c907f67e2bbc20f50157e08f35006fe2a8444d8ec9f5683e22f937045"
checksum = "c06d55b5ec4f9d9396fa99abaafa0688597395e57827dffd89731412ae90c9bf"
dependencies = [
"wast 27.0.0",
"wast 28.0.0",
]
[[package]]

4
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.27"
wat = "1.0.29"
libc = "0.2.60"
log = "0.4.8"
rayon = "1.2.1"
humantime = "2.0.0"
wasmparser = "0.68"
wasmparser = "0.69"
[dev-dependencies]
env_logger = "0.8.1"

2
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.68.0" }
regalloc = { version = "0.0.31" }
souper-ir = { version = "1", optional = true }
wast = { version = "27.0.0", optional = true }
wast = { version = "28.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

2
cranelift/peepmatic/Cargo.toml

@ -15,7 +15,7 @@ peepmatic-macro = { version = "0.68.0", path = "crates/macro" }
peepmatic-runtime = { version = "0.68.0", path = "crates/runtime", features = ["construct"] }
peepmatic-traits = { version = "0.68.0", path = "crates/traits" }
serde = { version = "1.0.105", features = ["derive"] }
wast = "27.0.0"
wast = "28.0.0"
z3 = { version = "0.7.1", features = ["static-link-z3"] }
[dev-dependencies]

2
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 = "27.0.0"
wast = "28.0.0"

2
cranelift/peepmatic/crates/runtime/Cargo.toml

@ -16,7 +16,7 @@ peepmatic-automata = { version = "0.68.0", path = "../automata", features = ["se
peepmatic-traits = { version = "0.68.0", path = "../traits" }
serde = { version = "1.0.105", features = ["derive"] }
thiserror = "1.0.15"
wast = { version = "27.0.0", optional = true }
wast = { version = "28.0.0", optional = true }
[dev-dependencies]
peepmatic-test-operator = { version = "0.68.0", path = "../test-operator" }

2
cranelift/peepmatic/crates/souper/Cargo.toml

@ -16,4 +16,4 @@ log = "0.4.8"
[dev-dependencies]
peepmatic = { path = "../..", version = "0.68.0" }
peepmatic-test-operator = { version = "0.68.0", path = "../test-operator" }
wast = "27.0.0"
wast = "28.0.0"

2
cranelift/peepmatic/crates/test-operator/Cargo.toml

@ -9,4 +9,4 @@ edition = "2018"
[dependencies]
peepmatic-traits = { version = "0.68.0", path = "../traits" }
serde = { version = "1.0.105", features = ["derive"] }
wast = "27.0.0"
wast = "28.0.0"

2
cranelift/wasm/Cargo.toml

@ -12,7 +12,7 @@ keywords = ["webassembly", "wasm"]
edition = "2018"
[dependencies]
wasmparser = { version = "0.68.0", default-features = false }
wasmparser = { version = "0.69.1", default-features = false }
cranelift-codegen = { path = "../codegen", version = "0.68.0", default-features = false }
cranelift-entity = { path = "../entity", version = "0.68.0" }
cranelift-frontend = { path = "../frontend", version = "0.68.0", default-features = false }

2
cranelift/wasm/src/translation_utils.rs

@ -110,7 +110,7 @@ pub struct InstanceTypeIndex(u32);
entity_impl!(InstanceTypeIndex);
/// An index of an entity.
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum EntityIndex {
/// Function index.

4
crates/c-api/src/extern.rs

@ -16,6 +16,10 @@ pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t {
Extern::Global(_) => crate::WASM_EXTERN_GLOBAL,
Extern::Table(_) => crate::WASM_EXTERN_TABLE,
Extern::Memory(_) => crate::WASM_EXTERN_MEMORY,
// FIXME(#2094)
Extern::Instance(_) => unimplemented!(),
Extern::Module(_) => unimplemented!(),
}
}

2
crates/debug/Cargo.toml

@ -13,7 +13,7 @@ edition = "2018"
[dependencies]
gimli = "0.23.0"
wasmparser = "0.68.0"
wasmparser = "0.69.0"
object = { version = "0.22.0", default-features = false, features = ["read", "write"] }
wasmtime-environ = { path = "../environ", version = "0.21.0" }
target-lexicon = { version = "0.11.0", default-features = false }

2
crates/environ/Cargo.toml

@ -16,7 +16,7 @@ anyhow = "1.0"
cranelift-codegen = { path = "../../cranelift/codegen", version = "0.68.0", features = ["enable-serde"] }
cranelift-entity = { path = "../../cranelift/entity", version = "0.68.0", features = ["enable-serde"] }
cranelift-wasm = { path = "../../cranelift/wasm", version = "0.68.0", features = ["enable-serde"] }
wasmparser = "0.68.0"
wasmparser = "0.69.0"
indexmap = { version = "1.0.2", features = ["serde-1"] }
thiserror = "1.0.4"
serde = { version = "1.0.94", features = ["derive"] }

27
crates/environ/src/module.rs

@ -346,6 +346,31 @@ impl Module {
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
index.index() < self.num_imported_globals
}
/// Returns an iterator of all the imports in this module, along with their
/// module name, field name, and type that's being imported.
pub fn imports(&self) -> impl Iterator<Item = (&str, Option<&str>, 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))),
_ => None,
})
}
/// Returns the type of an item based on its index
pub fn type_of(&self, index: EntityIndex) -> EntityType {
match index {
EntityIndex::Global(i) => EntityType::Global(self.globals[i]),
EntityIndex::Table(i) => EntityType::Table(self.table_plans[i].table),
EntityIndex::Memory(i) => EntityType::Memory(self.memory_plans[i].memory),
EntityIndex::Function(i) => EntityType::Function(self.functions[i]),
EntityIndex::Instance(i) => EntityType::Instance(self.instances[i]),
EntityIndex::Module(i) => EntityType::Module(self.modules[i]),
}
}
}
/// All types which are recorded for the entirety of a translation.
@ -376,7 +401,7 @@ pub struct ModuleSignature {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstanceSignature {
/// The name of what's being exported as well as its type signature.
pub exports: Vec<(String, EntityType)>,
pub exports: IndexMap<String, EntityType>,
}
mod passive_data_serde {

2
crates/environ/src/module_environ.rs

@ -857,7 +857,7 @@ and for re-adding support for interface types you can see this issue:
// instance.
Alias::Child { instance, export } => {
let ty = self.result.module.instances[instance];
match &self.types.instance_signatures[ty].exports[export].1 {
match &self.types.instance_signatures[ty].exports[export] {
EntityType::Global(g) => {
self.result.module.globals.push(g.clone());
self.result.module.num_imported_globals += 1;

4
crates/fuzzing/Cargo.toml

@ -12,8 +12,8 @@ arbitrary = { version = "0.4.1", features = ["derive"] }
env_logger = "0.8.1"
log = "0.4.8"
rayon = "1.2.1"
wasmparser = "0.68.0"
wasmprinter = "0.2.15"
wasmparser = "0.69.0"
wasmprinter = "0.2.16"
wasmtime = { path = "../wasmtime" }
wasmtime-wast = { path = "../wast" }
wasm-smith = "0.1.12"

2
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.68.0"
wasmparser = "0.69.0"
more-asserts = "0.2.1"
anyhow = "1.0"
cfg-if = "1.0"

2
crates/lightbeam/Cargo.toml

@ -24,7 +24,7 @@ more-asserts = "0.2.1"
smallvec = "1.0.0"
thiserror = "1.0.9"
typemap = "0.3"
wasmparser = "0.68.0"
wasmparser = "0.69.0"
[dev-dependencies]
lazy_static = "1.2"

2
crates/lightbeam/wasmtime/Cargo.toml

@ -13,6 +13,6 @@ edition = "2018"
[dependencies]
lightbeam = { path = "..", version = "0.21.0" }
wasmparser = "0.68"
wasmparser = "0.69"
cranelift-codegen = { path = "../../../cranelift/codegen", version = "0.68.0" }
wasmtime-environ = { path = "../../environ", version = "0.21.0" }

27
crates/runtime/src/export.rs

@ -1,13 +1,14 @@
use crate::vmcontext::{
VMCallerCheckedAnyfunc, VMContext, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition,
};
use crate::InstanceHandle;
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.
#[derive(Debug, Clone)]
pub enum Export {
pub enum Export<'a> {
/// A function export value.
Function(ExportFunction),
@ -19,6 +20,12 @@ pub enum Export {
/// A global export value.
Global(ExportGlobal),
/// An instance
Instance(&'a InstanceHandle),
/// A module
Module(&'a dyn Any),
}
/// A function export value.
@ -31,8 +38,8 @@ pub struct ExportFunction {
pub anyfunc: NonNull<VMCallerCheckedAnyfunc>,
}
impl From<ExportFunction> for Export {
fn from(func: ExportFunction) -> Export {
impl<'a> From<ExportFunction> for Export<'a> {
fn from(func: ExportFunction) -> Export<'a> {
Export::Function(func)
}
}
@ -48,8 +55,8 @@ pub struct ExportTable {
pub table: TablePlan,
}
impl From<ExportTable> for Export {
fn from(func: ExportTable) -> Export {
impl<'a> From<ExportTable> for Export<'a> {
fn from(func: ExportTable) -> Export<'a> {
Export::Table(func)
}
}
@ -65,8 +72,8 @@ pub struct ExportMemory {
pub memory: MemoryPlan,
}
impl From<ExportMemory> for Export {
fn from(func: ExportMemory) -> Export {
impl<'a> From<ExportMemory> for Export<'a> {
fn from(func: ExportMemory) -> Export<'a> {
Export::Memory(func)
}
}
@ -82,8 +89,8 @@ pub struct ExportGlobal {
pub global: Global,
}
impl From<ExportGlobal> for Export {
fn from(func: ExportGlobal) -> Export {
impl<'a> From<ExportGlobal> for Export<'a> {
fn from(func: ExportGlobal) -> Export<'a> {
Export::Global(func)
}
}

24
crates/runtime/src/imports.rs

@ -1,12 +1,21 @@
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.
///
/// Note that each of these fields are slices, not `PrimaryMap`. They should be
/// Note that some of these fields are slices, not `PrimaryMap`. They should be
/// stored in index-order as with the module that we're providing the imports
/// for, and indexing is all done the same way as the main module's index
/// spaces.
#[derive(Clone, Default)]
///
/// Also note that the way we compile modules means that for the module linking
/// proposal all `alias` directives should map to imported items. This means
/// that each of these items aren't necessarily directly imported, but may be
/// aliased.
#[derive(Default)]
pub struct Imports<'a> {
/// Resolved addresses for imported functions.
pub functions: &'a [VMFunctionImport],
@ -19,4 +28,15 @@ pub struct Imports<'a> {
/// Resolved addresses for imported globals.
pub globals: &'a [VMGlobalImport],
/// Resolved imported instances.
pub instances: PrimaryMap<InstanceIndex, InstanceHandle>,
/// Resolved imported modules.
///
/// Note that `Box<Any>` 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<ModuleIndex, Box<dyn Any>>,
}

22
crates/runtime/src/instance.rs

@ -28,8 +28,8 @@ 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, MemoryIndex, SignatureIndex,
TableElementType, TableIndex, WasmType,
ElemIndex, EntityIndex, FuncIndex, GlobalIndex, GlobalInit, InstanceIndex, MemoryIndex,
ModuleIndex, SignatureIndex, TableElementType, TableIndex, WasmType,
};
use wasmtime_environ::{ir, DataInitializer, Module, ModuleType, TableElements, VMOffsets};
@ -50,6 +50,15 @@ pub(crate) struct Instance {
/// WebAssembly table data.
tables: BoxedSlice<DefinedTableIndex, Table>,
/// Instances our module defined and their handles.
instances: PrimaryMap<InstanceIndex, InstanceHandle>,
/// Modules that are located in our index space.
///
/// For now these are `Box<Any>` so the caller can define the type of what a
/// module looks like.
modules: PrimaryMap<ModuleIndex, Box<dyn Any>>,
/// Passive elements in this instantiation. As `elem.drop`s happen, these
/// entries get removed. A missing entry is considered equivalent to an
/// empty slice.
@ -268,7 +277,7 @@ impl Instance {
}
/// 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();
@ -317,9 +326,8 @@ impl Instance {
}
.into(),
// FIXME(#2094)
EntityIndex::Instance(_index) => unimplemented!(),
EntityIndex::Module(_index) => unimplemented!(),
EntityIndex::Instance(index) => Export::Instance(&self.instances[*index]),
EntityIndex::Module(index) => Export::Module(&*self.modules[*index]),
}
}
@ -847,6 +855,8 @@ impl InstanceHandle {
passive_elements: Default::default(),
passive_data,
host_state,
instances: imports.instances,
modules: imports.modules,
vmctx: VMContext {},
};
let layout = instance.alloc_layout();

3
crates/wasmtime/Cargo.toml

@ -16,7 +16,7 @@ wasmtime-jit = { path = "../jit", version = "0.21.0" }
wasmtime-cache = { path = "../cache", version = "0.21.0", optional = true }
wasmtime-profiling = { path = "../profiling", version = "0.21.0" }
target-lexicon = { version = "0.11.0", default-features = false }
wasmparser = "0.68.0"
wasmparser = "0.69.0"
anyhow = "1.0.19"
region = "2.2.0"
libc = "0.2"
@ -29,6 +29,7 @@ wat = { version = "1.0.18", optional = true }
smallvec = "1.4.0"
serde = { version = "1.0.94", features = ["derive"] }
bincode = "1.2.1"
indexmap = "1.6"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = "0.3.7"

91
crates/wasmtime/src/externals.rs

@ -3,8 +3,8 @@ use crate::trampoline::{
};
use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
use crate::{
ExternRef, ExternType, Func, GlobalType, MemoryType, Mutability, Store, TableType, Trap,
ValType,
ExternRef, ExternType, Func, GlobalType, Instance, MemoryType, Module, Mutability, Store,
TableType, Trap, ValType,
};
use anyhow::{anyhow, bail, Result};
use std::mem;
@ -33,6 +33,10 @@ pub enum Extern {
Table(Table),
/// A WebAssembly linear memory.
Memory(Memory),
/// A WebAssembly instance.
Instance(Instance),
/// A WebAssembly module.
Module(Module),
}
impl Extern {
@ -76,6 +80,26 @@ impl Extern {
}
}
/// Returns the underlying `Instance`, if this external is a instance.
///
/// Returns `None` if this is not a instance.
pub fn into_instance(self) -> Option<Instance> {
match self {
Extern::Instance(instance) => Some(instance),
_ => None,
}
}
/// Returns the underlying `Module`, if this external is a module.
///
/// Returns `None` if this is not a module.
pub fn into_module(self) -> Option<Module> {
match self {
Extern::Module(module) => Some(module),
_ => None,
}
}
/// Returns the type associated with this `Extern`.
pub fn ty(&self) -> ExternType {
match self {
@ -83,6 +107,8 @@ impl Extern {
Extern::Memory(ft) => ExternType::Memory(ft.ty()),
Extern::Table(tt) => ExternType::Table(tt.ty()),
Extern::Global(gt) => ExternType::Global(gt.ty()),
Extern::Instance(i) => ExternType::Instance(i.ty()),
Extern::Module(m) => ExternType::Module(m.ty()),
}
}
@ -103,6 +129,13 @@ impl Extern {
wasmtime_runtime::Export::Table(t) => {
Extern::Table(Table::from_wasmtime_table(t, instance))
}
wasmtime_runtime::Export::Instance(i) => {
let handle = unsafe { instance.store.existing_instance_handle(i.clone()) };
Extern::Instance(Instance::from_wasmtime(handle))
}
wasmtime_runtime::Export::Module(m) => {
Extern::Module(m.downcast_ref::<Module>().unwrap().clone())
}
}
}
@ -112,6 +145,10 @@ impl Extern {
Extern::Global(g) => &g.instance.store,
Extern::Memory(m) => &m.instance.store,
Extern::Table(t) => &t.instance.store,
Extern::Instance(i) => i.store(),
// Modules don't live in stores right now, so they're compatible
// with all stores.
Extern::Module(_) => return true,
};
Store::same(my_store, store)
}
@ -122,6 +159,8 @@ impl Extern {
Extern::Table(_) => "table",
Extern::Memory(_) => "memory",
Extern::Global(_) => "global",
Extern::Instance(_) => "instance",
Extern::Module(_) => "module",
}
}
}
@ -150,6 +189,18 @@ impl From<Table> for Extern {
}
}
impl From<Instance> for Extern {
fn from(r: Instance) -> Self {
Extern::Instance(r)
}
}
impl From<Module> for Extern {
fn from(r: Module) -> Self {
Extern::Module(r)
}
}
/// A WebAssembly `global` value which can be read and written to.
///
/// A `global` in WebAssembly is sort of like a global variable within an
@ -294,11 +345,8 @@ impl Global {
}
}
pub(crate) fn matches_expected(&self, expected: &wasmtime_environ::wasm::Global) -> bool {
let actual = &self.wasmtime_export.global;
expected.ty == actual.ty
&& expected.wasm_ty == actual.wasm_ty
&& expected.mutability == actual.mutability
pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Global {
&self.wasmtime_export.global
}
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMGlobalImport {
@ -538,19 +586,8 @@ impl Table {
}
}
pub(crate) fn matches_expected(&self, ty: &wasmtime_environ::TablePlan) -> bool {
let expected = &ty.table;
let actual = &self.wasmtime_export.table.table;
expected.wasm_ty == actual.wasm_ty
&& expected.ty == actual.ty
&& expected.minimum <= actual.minimum
&& match expected.maximum {
Some(expected) => match actual.maximum {
Some(actual) => expected >= actual,
None => false,
},
None => true,
}
pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Table {
&self.wasmtime_export.table.table
}
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMTableImport {
@ -960,18 +997,8 @@ impl Memory {
}
}
pub(crate) fn matches_expected(&self, ty: &wasmtime_environ::MemoryPlan) -> bool {
let expected = &ty.memory;
let actual = &self.wasmtime_export.memory.memory;
expected.shared == actual.shared
&& expected.minimum <= actual.minimum
&& match expected.maximum {
Some(expected) => match actual.maximum {
Some(actual) => expected >= actual,
None => false,
},
None => true,
}
pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Memory {
&self.wasmtime_export.memory.memory
}
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMMemoryImport {

6
crates/wasmtime/src/func.rs

@ -798,10 +798,6 @@ impl Func {
&self.instance.store
}
pub(crate) fn matches_expected(&self, expected: VMSharedSignatureIndex) -> bool {
self.sig_index() == expected
}
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMFunctionImport {
unsafe {
let f = self.caller_checked_anyfunc();
@ -1503,7 +1499,6 @@ impl Caller<'_> {
return None;
}
let instance = InstanceHandle::from_vmctx(self.caller_vmctx);
let export = instance.lookup(name)?;
// Our `Weak` pointer is used only to break a cycle where `Store`
// stores instance handles which have this weak pointer as their
// custom host data. This function should only be invoke-able while
@ -1511,6 +1506,7 @@ 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))),

237
crates/wasmtime/src/instance.rs

@ -1,16 +1,23 @@
use crate::trampoline::StoreInstanceHandle;
use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap};
use crate::types::matching;
use crate::{
Engine, Export, Extern, ExternType, Func, Global, InstanceType, Memory, Module, Store, Table,
Trap,
};
use anyhow::{bail, Context, Error, Result};
use std::mem;
use std::sync::Arc;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::wasm::{
EntityIndex, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex, TableIndex,
EntityIndex, EntityType, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex,
TableIndex,
};
use wasmtime_environ::Initializer;
use wasmtime_jit::{CompiledModule, TypeTables};
use wasmtime_jit::TypeTables;
use wasmtime_runtime::{
Imports, InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable,
VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport,
Imports, InstanceHandle, InstantiationError, StackMapRegistry, VMContext,
VMExternRefActivationsTable, VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport,
VMTableImport,
};
/// Performs all low-level steps necessary for instantiation.
@ -35,17 +42,16 @@ use wasmtime_runtime::{
/// 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
/// fallible because type checks may fail.
fn instantiate<'a>(
store: &'a Store,
compiled_module: &'a CompiledModule,
all_modules: &'a [CompiledModule],
types: &'a TypeTables,
parent_modules: &PrimaryMap<ModuleIndex, &'a CompiledModule>,
define_import: &mut dyn FnMut(&EntityIndex, &mut ImportsBuilder<'a>) -> Result<()>,
fn instantiate(
store: &Store,
module: &Module,
parent_modules: &PrimaryMap<ModuleIndex, Module>,
define_import: &mut dyn FnMut(&EntityIndex, &mut ImportsBuilder<'_>) -> Result<()>,
) -> Result<StoreInstanceHandle, Error> {
let compiled_module = module.compiled_module();
let env_module = compiled_module.module();
let mut imports = ImportsBuilder::new(env_module, types, store);
let mut imports = ImportsBuilder::new(store, module);
for initializer in env_module.initializers.iter() {
match initializer {
// Definition of an import depends on how our parent is providing
@ -67,24 +73,28 @@ fn instantiate<'a>(
// 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]);
imports.modules.push(parent_modules[*idx].clone());
}
// Turns out defining any kind of module is pretty easy, we're just
// slinging around pointers.
Initializer::DefineModule(idx) => {
imports.modules.push(&all_modules[*idx]);
imports.modules.push(module.submodule(*idx));
}
// Here we lookup our instance handle, ask it for the nth export,
// and then push that item into our own index space. We eschew
// type-checking since only valid modules reach this point.
//
// Note that the unsafety here is because we're asserting that the
// 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 handle = &imports.instances[*instance];
let export_index = &handle.module().exports[*export];
let item = Extern::from_wasmtime_export(
handle.lookup_by_declaration(export_index),
handle.clone(),
unsafe { store.existing_instance_handle(handle.clone()) },
);
imports.push_extern(&item);
}
@ -100,14 +110,16 @@ fn instantiate<'a>(
// to be a DAG. Additionally the recursion should also be bounded
// due to validation. We may one day need to make this an iterative
// loop, however.
//
// Also note that there's some unsafety here around cloning
// `InstanceHandle` because the handle may not live long enough, but
// 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 module_to_instantiate = imports.modules[*module];
let mut args = args.iter();
let handle = instantiate(
store,
module_to_instantiate,
all_modules,
types,
&imports.modules[*module],
&imports.modules,
&mut |_, builder| {
match *args.next().unwrap() {
@ -124,16 +136,18 @@ fn instantiate<'a>(
builder.memories.push(imports.memories[i]);
}
EntityIndex::Module(i) => {
builder.modules.push(imports.modules[i]);
builder.modules.push(imports.modules[i].clone());
}
EntityIndex::Instance(i) => {
builder.instances.push(imports.instances[i].clone());
builder
.instances
.push(unsafe { imports.instances[i].clone() });
}
}
Ok(())
},
)?;
imports.instances.push(handle);
imports.instances.push(unsafe { (*handle).clone() });
}
}
}
@ -141,21 +155,21 @@ fn instantiate<'a>(
// 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.imports();
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`.
store.register_module(compiled_module, types);
store.register_module(module);
let config = store.engine().config();
let instance = unsafe {
let instance = compiled_module.instantiate(
imports,
&store.lookup_shared_signature(types),
&store.lookup_shared_signature(module.types()),
config.memory_creator.as_ref().map(|a| a as _),
store.interrupts(),
Box::new(()),
Box::new(module.types().clone()),
store.externref_activations_table() as *const VMExternRefActivationsTable as *mut _,
store.stack_map_registry() as *const StackMapRegistry as *mut _,
)?;
@ -230,7 +244,6 @@ fn instantiate<'a>(
#[derive(Clone)]
pub struct Instance {
pub(crate) handle: StoreInstanceHandle,
module: Module,
}
impl Instance {
@ -294,16 +307,7 @@ impl Instance {
// Perform some pre-flight checks before we get into the meat of
// instantiation.
let expected = module
.compiled_module()
.module()
.initializers
.iter()
.filter(|e| match e {
Initializer::Import { .. } => true,
_ => false,
})
.count();
let expected = module.compiled_module().module().imports().count();
if expected != imports.len() {
bail!("expected {} imports, found {}", expected, imports.len());
}
@ -314,22 +318,34 @@ impl Instance {
}
let mut imports = imports.iter();
let handle = instantiate(
store,
module.compiled_module(),
module.all_compiled_modules(),
module.types(),
&PrimaryMap::new(),
&mut |idx, builder| {
let import = imports.next().expect("already checked the length");
builder.define_extern(idx, import)
},
)?;
let handle = instantiate(store, module, &PrimaryMap::new(), &mut |idx, builder| {
let import = imports.next().expect("already checked the length");
builder.define_extern(idx, import)
})?;
Ok(Instance {
handle,
module: module.clone(),
})
Ok(Instance { handle })
}
pub(crate) fn from_wasmtime(handle: StoreInstanceHandle) -> Instance {
Instance { handle }
}
/// 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::<Arc<TypeTables>>()
.unwrap();
for (name, index) in module.exports.iter() {
ty.add_named_export(
name,
ExternType::from_wasmtime(types, &module.type_of(*index)),
);
}
ty
}
/// Returns the associated [`Store`] that this `Instance` is compiled into.
@ -400,24 +416,20 @@ struct ImportsBuilder<'a> {
tables: PrimaryMap<TableIndex, VMTableImport>,
memories: PrimaryMap<MemoryIndex, VMMemoryImport>,
globals: PrimaryMap<GlobalIndex, VMGlobalImport>,
instances: PrimaryMap<InstanceIndex, StoreInstanceHandle>,
modules: PrimaryMap<ModuleIndex, &'a CompiledModule>,
instances: PrimaryMap<InstanceIndex, InstanceHandle>,
modules: PrimaryMap<ModuleIndex, Module>,
module: &'a wasmtime_environ::Module,
store: &'a Store,
types: &'a TypeTables,
matcher: matching::MatchCx<'a>,
}
impl<'a> ImportsBuilder<'a> {
fn new(
module: &'a wasmtime_environ::Module,
types: &'a TypeTables,
store: &'a Store,
) -> ImportsBuilder<'a> {
fn new(store: &'a Store, module: &'a Module) -> ImportsBuilder<'a> {
let types = module.types();
let module = module.compiled_module().module();
ImportsBuilder {
module,
store,
types,
matcher: matching::MatchCx { store, types },
functions: PrimaryMap::with_capacity(module.num_imported_funcs),
tables: PrimaryMap::with_capacity(module.num_imported_tables),
memories: PrimaryMap::with_capacity(module.num_imported_memories),
@ -428,59 +440,38 @@ impl<'a> ImportsBuilder<'a> {
}
fn define_extern(&mut self, expected: &EntityIndex, actual: &Extern) -> Result<()> {
match *expected {
EntityIndex::Table(i) => {
self.tables.push(match actual {
Extern::Table(e) if e.matches_expected(&self.module.table_plans[i]) => {
e.vmimport()
}
Extern::Table(_) => bail!("table types incompatible"),
_ => bail!("expected table, but found {}", actual.desc()),
});
}
EntityIndex::Memory(i) => {
self.memories.push(match actual {
Extern::Memory(e) if e.matches_expected(&self.module.memory_plans[i]) => {
e.vmimport()
}
Extern::Memory(_) => bail!("memory types incompatible"),
_ => bail!("expected memory, but found {}", actual.desc()),
});
}
EntityIndex::Global(i) => {
self.globals.push(match actual {
Extern::Global(e) if e.matches_expected(&self.module.globals[i]) => {
e.vmimport()
}
Extern::Global(_) => bail!("global types incompatible"),
_ => bail!("expected global, but found {}", actual.desc()),
});
}
EntityIndex::Function(i) => {
let func = match actual {
Extern::Func(e) => e,
_ => bail!("expected function, but found {}", actual.desc()),
};
// Look up the `i`th function's type from the module in our
// signature registry. If it's not present then we have no
// functions registered with that type, so `func` is guaranteed
// to not match.
let ty = self
.store
.signatures()
.borrow()
.lookup(&self.types.wasm_signatures[self.module.functions[i]])
.ok_or_else(|| anyhow::format_err!("function types incompatible"))?;
if !func.matches_expected(ty) {
bail!("function types incompatible");
}
self.functions.push(func.vmimport());
}
// FIXME(#2094)
EntityIndex::Module(_i) => unimplemented!(),
EntityIndex::Instance(_i) => unimplemented!(),
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.push_extern(actual);
Ok(())
}
@ -498,15 +489,27 @@ impl<'a> ImportsBuilder<'a> {
Extern::Memory(i) => {
self.memories.push(i.vmimport());
}
Extern::Instance(i) => {
debug_assert!(Store::same(i.store(), self.matcher.store));
self.instances.push(unsafe { (*i.handle).clone() });
}
Extern::Module(m) => {
self.modules.push(m.clone());
}
}
}
fn imports(&self) -> Imports<'_> {
fn build(&mut self) -> Imports<'_> {
Imports {
tables: self.tables.values().as_slice(),
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(),
}
}
}

8
crates/wasmtime/src/linker.rs

@ -58,6 +58,8 @@ enum ImportKind {
Global(GlobalType),
Memory,
Table,
Module,
Instance,
}
impl Linker {
@ -516,10 +518,8 @@ impl Linker {
ExternType::Global(f) => ImportKind::Global(f),
ExternType::Memory(_) => ImportKind::Memory,
ExternType::Table(_) => ImportKind::Table,
// FIXME(#2094)
ExternType::Module(_) => unimplemented!(),
ExternType::Instance(_) => unimplemented!(),
ExternType::Module(_) => ImportKind::Module,
ExternType::Instance(_) => ImportKind::Instance,
}
}

63
crates/wasmtime/src/module.rs

@ -1,5 +1,5 @@
use crate::types::{EntityType, ExportType, ExternType, ImportType};
use crate::Engine;
use crate::types::{ExportType, ExternType, ImportType};
use crate::{Engine, ModuleType};
use anyhow::{bail, Context, Result};
use bincode::Options;
use std::hash::Hash;
@ -86,7 +86,7 @@ pub struct Module {
}
pub(crate) struct ModuleData {
pub(crate) types: TypeTables,
pub(crate) types: Arc<TypeTables>,
pub(crate) modules: Vec<CompiledModule>,
}
@ -258,6 +258,7 @@ impl Module {
&*engine.config().profiler,
)?;
let types = Arc::new(types);
Ok(Module {
engine: engine.clone(),
index: 0,
@ -291,6 +292,23 @@ impl Module {
Ok(())
}
/// Returns the type signature of this module.
pub fn ty(&self) -> ModuleType {
let mut sig = ModuleType::new();
let env_module = self.compiled_module().module();
let types = self.types();
for (module, field, ty) in env_module.imports() {
sig.add_named_import(module, field, ExternType::from_wasmtime(types, &ty));
}
for (name, index) in env_module.exports.iter() {
sig.add_named_export(
name,
ExternType::from_wasmtime(types, &env_module.type_of(*index)),
);
}
sig
}
/// Serialize compilation artifacts to the buffer. See also `deseriaize`.
pub fn serialize(&self) -> Result<Vec<u8>> {
let artifacts = (
@ -300,7 +318,7 @@ impl Module {
.iter()
.map(|i| i.compilation_artifacts())
.collect::<Vec<_>>(),
&self.data.types,
&*self.data.types,
self.index,
);
@ -333,6 +351,7 @@ impl Module {
&*engine.config().profiler,
)?;
let types = Arc::new(types);
Ok(Module {
engine: engine.clone(),
index,
@ -344,11 +363,16 @@ impl Module {
&self.data.modules[self.index]
}
pub(crate) fn all_compiled_modules(&self) -> &[CompiledModule] {
&self.data.modules
pub(crate) fn submodule(&self, index: usize) -> Module {
assert!(index < self.data.modules.len());
Module {
engine: self.engine.clone(),
data: self.data.clone(),
index,
}
}
pub(crate) fn types(&self) -> &TypeTables {
pub(crate) fn types(&self) -> &Arc<TypeTables> {
&self.data.types
}
@ -433,20 +457,10 @@ impl Module {
&'module self,
) -> impl ExactSizeIterator<Item = ImportType<'module>> + 'module {
let module = self.compiled_module().module();
let types = self.types();
module
.initializers
.iter()
.filter_map(move |initializer| match initializer {
wasmtime_environ::Initializer::Import {
module,
field,
index,
} => {
let ty = EntityType::new(index, self);
Some(ImportType::new(module, field.as_deref(), ty))
}
_ => None,
})
.imports()
.map(move |(module, field, ty)| ImportType::new(module, field, ty, types))
.collect::<Vec<_>>()
.into_iter()
}
@ -509,9 +523,9 @@ impl Module {
&'module self,
) -> impl ExactSizeIterator<Item = ExportType<'module>> + 'module {
let module = self.compiled_module().module();
let types = self.types();
module.exports.iter().map(move |(name, entity_index)| {
let ty = EntityType::new(entity_index, self);
ExportType::new(name, ty)
ExportType::new(name, module.type_of(*entity_index), types)
})
}
@ -561,7 +575,10 @@ impl Module {
pub fn get_export<'module>(&'module self, name: &'module str) -> Option<ExternType> {
let module = self.compiled_module().module();
let entity_index = module.exports.get(name)?;
Some(EntityType::new(entity_index, self).extern_type())
Some(ExternType::from_wasmtime(
self.types(),
&module.type_of(*entity_index),
))
}
/// Returns the [`Engine`] that this [`Module`] was compiled by.

18
crates/wasmtime/src/store.rs

@ -1,7 +1,7 @@
use crate::frame_info::StoreFrameInfo;
use crate::sig_registry::SignatureRegistry;
use crate::trampoline::StoreInstanceHandle;
use crate::Engine;
use crate::{Engine, Module};
use anyhow::{bail, Result};
use std::any::Any;
use std::cell::RefCell;
@ -147,7 +147,7 @@ impl Store {
}
}
pub(crate) fn register_module(&self, module: &CompiledModule, types: &TypeTables) {
pub(crate) fn register_module(&self, module: &Module) {
// All modules register their JIT code in a store for two reasons
// currently:
//
@ -158,18 +158,18 @@ impl Store {
// * Second when generating a backtrace we'll use this mapping to
// only generate wasm frames for instruction pointers that fall
// within jit code.
self.register_jit_code(module);
self.register_jit_code(module.compiled_module());
// We need to know about all the stack maps of all instantiated modules
// so when performing a GC we know about all wasm frames that we find
// on the stack.
self.register_stack_maps(module);
self.register_stack_maps(module.compiled_module());
// Signatures are loaded into our `SignatureRegistry` here
// once-per-module (and once-per-signature). This allows us to create
// a `Func` wrapper for any function in the module, which requires that
// we know about the signature and trampoline for all instances.
self.register_signatures(module, types);
self.register_signatures(module);
// And finally with a module being instantiated into this `Store` we
// need to preserve its jit-code. References to this module's code and
@ -178,7 +178,7 @@ impl Store {
self.inner
.modules
.borrow_mut()
.insert(ArcModuleCode(module.code().clone()));
.insert(ArcModuleCode(module.compiled_module().code().clone()));
}
fn register_jit_code(&self, module: &CompiledModule) {
@ -205,10 +205,10 @@ impl Store {
}));
}
fn register_signatures(&self, module: &CompiledModule, types: &TypeTables) {
let trampolines = module.trampolines();
fn register_signatures(&self, module: &Module) {
let trampolines = module.compiled_module().trampolines();
let mut signatures = self.signatures().borrow_mut();
for (index, wasm) in types.wasm_signatures.iter() {
for (index, wasm) in module.types().wasm_signatures.iter() {
signatures.register(wasm, trampolines[index]);
}
}

117
crates/wasmtime/src/types.rs

@ -1,7 +1,9 @@
use crate::Module;
use std::fmt;
use wasmtime_environ::wasm::WasmFuncType;
use wasmtime_environ::wasm::{EntityType, WasmFuncType};
use wasmtime_environ::{ir, wasm};
use wasmtime_jit::TypeTables;
pub(crate) mod matching;
// Type Representations
@ -196,23 +198,25 @@ impl ExternType {
(Instance(InstanceType) instance unwrap_instance)
}
fn from_wasmtime(module: &Module, ty: &wasmtime_environ::wasm::EntityType) -> ExternType {
use wasmtime_environ::wasm::EntityType;
pub(crate) fn from_wasmtime(
types: &TypeTables,
ty: &wasmtime_environ::wasm::EntityType,
) -> ExternType {
match ty {
EntityType::Function(idx) => {
let sig = &module.types().wasm_signatures[*idx];
let sig = &types.wasm_signatures[*idx];
FuncType::from_wasm_func_type(sig).into()
}
EntityType::Global(ty) => GlobalType::from_wasmtime_global(ty).into(),
EntityType::Memory(ty) => MemoryType::from_wasmtime_memory(ty).into(),
EntityType::Table(ty) => TableType::from_wasmtime_table(ty).into(),
EntityType::Module(ty) => {
let ty = &module.types().module_signatures[*ty];
ModuleType::from_wasmtime(module, ty).into()
let ty = &types.module_signatures[*ty];
ModuleType::from_wasmtime(types, ty).into()
}
EntityType::Instance(ty) => {
let ty = &module.types().instance_signatures[*ty];
InstanceType::from_wasmtime(module, ty).into()
let ty = &types.instance_signatures[*ty];
InstanceType::from_wasmtime(types, ty).into()
}
EntityType::Event(_) => unimplemented!("wasm event support"),
}
@ -490,14 +494,14 @@ impl ModuleType {
}
pub(crate) fn from_wasmtime(
module: &Module,
types: &TypeTables,
ty: &wasmtime_environ::ModuleSignature,
) -> ModuleType {
let exports = &module.types().instance_signatures[ty.exports].exports;
let exports = &types.instance_signatures[ty.exports].exports;
ModuleType {
exports: exports
.iter()
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(module, ty)))
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(types, ty)))
.collect(),
imports: ty
.imports
@ -506,7 +510,7 @@ impl ModuleType {
(
m.to_string(),
name.as_ref().map(|n| n.to_string()),
ExternType::from_wasmtime(module, ty),
ExternType::from_wasmtime(types, ty),
)
})
.collect(),
@ -548,83 +552,19 @@ impl InstanceType {
}
pub(crate) fn from_wasmtime(
module: &Module,
types: &TypeTables,
ty: &wasmtime_environ::InstanceSignature,
) -> InstanceType {
InstanceType {
exports: ty
.exports
.iter()
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(module, ty)))
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(types, ty)))
.collect(),
}
}
}
// Entity Types
#[derive(Clone)]
pub(crate) enum EntityType<'module> {
Function(&'module wasm::WasmFuncType),
Table(&'module wasm::Table),
Memory(&'module wasm::Memory),
Global(&'module wasm::Global),
Module {
ty: &'module wasmtime_environ::ModuleSignature,
module: &'module Module,
},
Instance {
ty: &'module wasmtime_environ::InstanceSignature,
module: &'module Module,
},
}
impl<'module> EntityType<'module> {
/// Translate from a `EntityIndex` into an `ExternType`.
pub(crate) fn new(
entity_index: &wasm::EntityIndex,
module: &'module Module,
) -> EntityType<'module> {
let env_module = module.compiled_module().module();
match entity_index {
wasm::EntityIndex::Function(func_index) => {
let sig_index = env_module.functions[*func_index];
let sig = &module.types().wasm_signatures[sig_index];
EntityType::Function(sig)
}
wasm::EntityIndex::Table(table_index) => {
EntityType::Table(&env_module.table_plans[*table_index].table)
}
wasm::EntityIndex::Memory(memory_index) => {
EntityType::Memory(&env_module.memory_plans[*memory_index].memory)
}
wasm::EntityIndex::Global(global_index) => {
EntityType::Global(&env_module.globals[*global_index])
}
wasm::EntityIndex::Module(idx) => {
let ty = &module.types().module_signatures[env_module.modules[*idx]];
EntityType::Module { ty, module }
}
wasm::EntityIndex::Instance(idx) => {
let ty = &module.types().instance_signatures[env_module.instances[*idx]];
EntityType::Instance { ty, module }
}
}
}
/// Convert this `EntityType` to an `ExternType`.
pub(crate) fn extern_type(&self) -> ExternType {
match self {
EntityType::Function(sig) => FuncType::from_wasm_func_type(sig).into(),
EntityType::Table(table) => TableType::from_wasmtime_table(table).into(),
EntityType::Memory(memory) => MemoryType::from_wasmtime_memory(memory).into(),
EntityType::Global(global) => GlobalType::from_wasmtime_global(global).into(),
EntityType::Instance { module, ty } => InstanceType::from_wasmtime(module, ty).into(),
EntityType::Module { module, ty } => ModuleType::from_wasmtime(module, ty).into(),
}
}
}
// Import Types
/// A descriptor for an imported value into a wasm module.
@ -647,7 +587,7 @@ pub struct ImportType<'module> {
#[derive(Clone)]
enum EntityOrExtern<'a> {
Entity(EntityType<'a>),
Entity(EntityType, &'a TypeTables),
Extern(&'a ExternType),
}
@ -657,12 +597,13 @@ impl<'module> ImportType<'module> {
pub(crate) fn new(
module: &'module str,
name: Option<&'module str>,
ty: EntityType<'module>,
ty: EntityType,
types: &'module TypeTables,
) -> ImportType<'module> {
ImportType {
module,
name,
ty: EntityOrExtern::Entity(ty),
ty: EntityOrExtern::Entity(ty, types),
}
}
@ -683,7 +624,7 @@ impl<'module> ImportType<'module> {
/// Returns the expected type of this import.
pub fn ty(&self) -> ExternType {
match &self.ty {
EntityOrExtern::Entity(e) => e.extern_type(),
EntityOrExtern::Entity(e, types) => ExternType::from_wasmtime(types, e),
EntityOrExtern::Extern(e) => (*e).clone(),
}
}
@ -719,10 +660,14 @@ pub struct ExportType<'module> {
impl<'module> ExportType<'module> {
/// Creates a new export which is exported with the given `name` and has the
/// given `ty`.
pub(crate) fn new(name: &'module str, ty: EntityType<'module>) -> ExportType<'module> {
pub(crate) fn new(
name: &'module str,
ty: EntityType,
types: &'module TypeTables,
) -> ExportType<'module> {
ExportType {
name,
ty: EntityOrExtern::Entity(ty),
ty: EntityOrExtern::Entity(ty, types),
}
}
@ -734,7 +679,7 @@ impl<'module> ExportType<'module> {
/// Returns the type of this export.
pub fn ty(&self) -> ExternType {
match &self.ty {
EntityOrExtern::Entity(e) => e.extern_type(),
EntityOrExtern::Entity(e, types) => ExternType::from_wasmtime(types, e),
EntityOrExtern::Extern(e) => (*e).clone(),
}
}

195
crates/wasmtime/src/types/matching.rs

@ -0,0 +1,195 @@
use crate::Store;
use std::sync::Arc;
use wasmtime_environ::wasm::{
EntityType, Global, InstanceTypeIndex, Memory, ModuleTypeIndex, SignatureIndex, Table,
};
use wasmtime_jit::TypeTables;
pub struct MatchCx<'a> {
pub types: &'a TypeTables,
pub store: &'a Store,
}
impl MatchCx<'_> {
pub fn global(&self, expected: &Global, actual: &crate::Global) -> bool {
self.global_ty(expected, actual.wasmtime_ty())
}
fn global_ty(&self, expected: &Global, actual: &Global) -> bool {
expected.ty == actual.ty
&& expected.wasm_ty == actual.wasm_ty
&& expected.mutability == actual.mutability
}
pub fn table(&self, expected: &Table, actual: &crate::Table) -> bool {
self.table_ty(expected, actual.wasmtime_ty())
}
fn table_ty(&self, expected: &Table, actual: &Table) -> bool {
expected.wasm_ty == actual.wasm_ty
&& expected.ty == actual.ty
&& expected.minimum <= actual.minimum
&& match expected.maximum {
Some(expected) => match actual.maximum {
Some(actual) => expected >= actual,
None => false,
},
None => true,
}
}
pub fn memory(&self, expected: &Memory, actual: &crate::Memory) -> bool {
self.memory_ty(expected, actual.wasmtime_ty())
}
fn memory_ty(&self, expected: &Memory, actual: &Memory) -> bool {
expected.shared == actual.shared
&& expected.minimum <= actual.minimum
&& match expected.maximum {
Some(expected) => match actual.maximum {
Some(actual) => expected >= actual,
None => false,
},
None => true,
}
}
pub fn func(&self, expected: SignatureIndex, actual: &crate::Func) -> bool {
match self
.store
.signatures()
.borrow()
.lookup(&self.types.wasm_signatures[expected])
{
Some(idx) => actual.sig_index() == idx,
// If our expected signature isn't registered, then there's no way
// that `actual` can match it.
None => false,
}
}
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::<Arc<TypeTables>>()
.unwrap(),
|name| module.exports.get(name).map(|idx| module.type_of(*idx)),
)
}
/// Validates that the type signature of `actual` matches the `expected`
/// module type signature.
pub fn module(&self, expected: ModuleTypeIndex, actual: &crate::Module) -> bool {
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))
})
}
/// Validates that the `actual_imports` list of module imports matches the
/// `expected` module type signature.
///
/// Types specified in `actual_imports` are relative to `actual_types`.
fn imports_match<'a>(
&self,
expected: ModuleTypeIndex,
actual_types: &TypeTables,
mut actual_imports: impl Iterator<Item = (&'a str, Option<&'a str>, EntityType)>,
) -> bool {
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,
};
if !self.extern_ty_matches(expected, &ty, actual_types) {
return false;
}
}
actual_imports.next().is_none()
}
/// Validates that all exports in `expected` are defined by `lookup` within
/// `actual_types`.
fn exports_match(
&self,
expected: InstanceTypeIndex,
actual_types: &TypeTables,
lookup: impl Fn(&str) -> Option<EntityType>,
) -> bool {
// 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,
},
)
}
/// Validates that the `expected` entity matches the `actual_ty` defined
/// within `actual_types`.
fn extern_ty_matches(
&self,
expected: &EntityType,
actual_ty: &EntityType,
actual_types: &TypeTables,
) -> bool {
match expected {
EntityType::Global(expected) => match actual_ty {
EntityType::Global(actual) => self.global_ty(expected, actual),
_ => false,
},
EntityType::Table(expected) => match actual_ty {
EntityType::Table(actual) => self.table_ty(expected, actual),
_ => false,
},
EntityType::Memory(expected) => match actual_ty {
EntityType::Memory(actual) => self.memory_ty(expected, actual),
_ => false,
},
EntityType::Function(expected) => match *actual_ty {
EntityType::Function(actual) => {
self.types.wasm_signatures[*expected] == actual_types.wasm_signatures[actual]
}
_ => false,
},
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()
})
}
_ => false,
},
EntityType::Module(expected) => match actual_ty {
EntityType::Module(actual) => {
let expected_module_sig = &self.types.module_signatures[*expected];
let actual_module_sig = &actual_types.module_signatures[*actual];
let actual_instance_sig =
&actual_types.instance_signatures[actual_module_sig.exports];
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_instance_sig.exports.get(name).cloned()
})
}
_ => false,
},
EntityType::Event(_) => unimplemented!(),
}
}
}

2
crates/wast/Cargo.toml

@ -13,7 +13,7 @@ edition = "2018"
[dependencies]
anyhow = "1.0.19"
wasmtime = { path = "../wasmtime", version = "0.21.0", default-features = false }
wast = "27.0.0"
wast = "28.0.0"
[badges]
maintenance = { status = "actively-developed" }

5
scripts/publish.rs

@ -293,7 +293,10 @@ fn verify(crates: &[Crate]) {
// Vendor witx which wasn't vendored because it's a path dependency, but
// it'll need to be in our directory registry for crates that depend on it.
let witx = crates.iter().find(|c| c.name == "witx").unwrap();
let witx = crates
.iter()
.find(|c| c.name == "witx" && c.manifest.iter().any(|p| p == "wasi-common"))
.unwrap();
verify_and_vendor(&witx);
for krate in crates {

4
tests/all/wast.rs

@ -23,8 +23,8 @@ fn run_wast(wast: &str, strategy: Strategy) -> anyhow::Result<()> {
let mut cfg = Config::new();
cfg.wasm_simd(simd)
.wasm_bulk_memory(bulk_mem)
.wasm_reference_types(reftypes)
.wasm_multi_memory(multi_memory)
.wasm_reference_types(reftypes || module_linking)
.wasm_multi_memory(multi_memory || module_linking)
.wasm_module_linking(module_linking)
.strategy(strategy)?
.cranelift_debug_verifier(true);

33
tests/misc_testsuite/module-linking/alias.wast

@ -54,7 +54,38 @@
)
(assert_return (invoke "get") (i32.const 4))
;; TODO instances/modules -- needs import/export of modules/instances to work
;; modules
(module
(module $m
(module $sub (export "module")
(func $f (export "") (result i32)
i32.const 5))
)
(instance $a (instantiate $m))
(instance $b (instantiate $a.$sub))
(alias $b.$f (instance $b) (func 0))
(func (export "get") (result i32)
call $b.$f)
)
(assert_return (invoke "get") (i32.const 5))
;; instances
(module
(module $m
(module $sub
(func $f (export "") (result i32)
i32.const 6))
(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)
)
(assert_return (invoke "get") (i32.const 6))
;; alias parent -- type
(module

348
tests/misc_testsuite/module-linking/import-subtyping.wast

@ -0,0 +1,348 @@
;; subsets of imports
(module $a
(module (export "m")
(func (export ""))
(func (export "a"))
(global (export "b") i32 (i32.const 0))
)
)
(module
(import "a" "m" (module))
(import "a" "m" (module (export "" (func))))
(import "a" "m" (module (export "a" (func))))
(import "a" "m" (module (export "b" (global i32))))
(import "a" "m" (module
(export "" (func))
(export "a" (func))
))
(import "a" "m" (module
(export "a" (func))
(export "" (func))
))
(import "a" "m" (module
(export "a" (func))
(export "" (func))
(export "b" (global i32))
))
(import "a" "m" (module
(export "b" (global i32))
(export "a" (func))
(export "" (func))
))
)
;; functions
(module $a
(module (export "m")
(func (export ""))))
(module
(import "a" "m" (module))
(import "a" "m" (module (export "" (func))))
)
(assert_unlinkable
(module (import "a" "m" (module (export "" (func (param i32))))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func (result i32))))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (global i32)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 funcref)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 1)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (instance)))))
"module types incompatible")
(module $a
(module (export "m")
(global (export "") i32 (i32.const 0))))
;; globals
(module
(import "a" "m" (module))
(import "a" "m" (module (export "" (global i32))))
)
(assert_unlinkable
(module
(import "a" "m" (module (export "" (global (mut i32)))))
)
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (global f32)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 funcref)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 1)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (instance)))))
"module types incompatible")
;; tables
(module $a
(module (export "m")
(table (export "") 1 funcref)
(table (export "max") 1 10 funcref)
)
)
(module
(import "a" "m" (module))
(import "a" "m" (module (export "" (table 1 funcref))))
(import "a" "m" (module (export "" (table 0 funcref))))
(import "a" "m" (module (export "max" (table 1 10 funcref))))
(import "a" "m" (module (export "max" (table 0 10 funcref))))
(import "a" "m" (module (export "max" (table 0 11 funcref))))
(import "a" "m" (module (export "max" (table 0 funcref))))
)
(assert_unlinkable
(module (import "a" "m" (module (export "" (global f32)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 2 funcref)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 10 funcref)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "max" (table 2 10 funcref)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "max" (table 1 9 funcref)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 1)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (instance)))))
"module types incompatible")
;; memories
(module $a
(module (export "m")
(memory (export "") 1)
(memory (export "max") 1 10)
)
)
(module
(import "a" "m" (module))
(import "a" "m" (module (export "" (memory 1))))
(import "a" "m" (module (export "" (memory 0))))
(import "a" "m" (module (export "max" (memory 1 10))))
(import "a" "m" (module (export "max" (memory 0 10))))
(import "a" "m" (module (export "max" (memory 0 11))))
(import "a" "m" (module (export "max" (memory 0))))
)
(assert_unlinkable
(module (import "a" "m" (module (export "" (global f32)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 funcref)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 2)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 1 10)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "max" (memory 2 10)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "max" (memory 2)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (instance)))))
"module types incompatible")
;; modules
(module $a
(module (export "m")
;; export nothing
(module (export "a"))
;; export one thing
(module (export "b")
(func (export ""))
)
;; export a mixture
(module (export "c")
(func (export "a"))
(func (export "b") (result i32)
i32.const 0)
(global (export "c") i32 (i32.const 0))
)
;; import one thing
(module (export "d")
(import "" (func))
)
;; import a mixture
(module (export "e")
(import "" (func))
(import "" (func))
(import "" (global i32))
)
)
)
(module
(import "a" "m" (module))
(import "a" "m" (module (export "a" (module))))
(import "a" "m" (module (export "b" (module))))
(import "a" "m" (module (export "b" (module (export "" (func))))))
(import "a" "m" (module (export "c" (module))))
(import "a" "m" (module (export "c" (module
(export "a" (func))
))))
(import "a" "m" (module (export "c" (module
(export "a" (func))
(export "b" (func (result i32)))
))))
(import "a" "m" (module (export "c" (module
(export "c" (global i32))
))))
(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))
(import "a" (func))
(import "z" (global i32))
))))
)
(assert_unlinkable
(module (import "a" "m" (module (export "" (module (export "a" (func)))))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "d" (module)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "d" (module (import "" (module)))))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (global f32)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 funcref)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 2)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module (export "foo" (func)))))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (instance)))))
"module types incompatible")
;; instances
(module $a
;; export nothing
(module $m1)
(instance (export "a") (instantiate $m1))
;; export one thing
(module $m2
(func (export ""))
)
(instance (export "b") (instantiate $m2))
;; export a mixture
(module $m3
(func (export "a"))
(func (export "b") (result i32)
i32.const 0)
(global (export "c") i32 (i32.const 0))
)
(instance (export "c") (instantiate $m3))
(module (export "m")
;; export one thing
(module $m2
(func (export ""))
)
(instance (export "i") (instantiate $m2))
)
)
(module
(import "a" "a" (instance))
(import "a" "b" (instance))
(import "a" "b" (instance (export "" (func))))
(import "a" "c" (instance))
(import "a" "c" (instance (export "a" (func))))
(import "a" "c" (instance (export "b" (func (result i32)))))
(import "a" "c" (instance (export "c" (global i32))))
(import "a" "c" (instance
(export "a" (func))
(export "b" (func (result i32)))
(export "c" (global i32))
))
(import "a" "c" (instance
(export "c" (global i32))
(export "a" (func))
))
(import "a" "m" (module (export "i" (instance))))
(import "a" "m" (module (export "i" (instance (export "" (func))))))
)
(assert_unlinkable
(module (import "a" "a" (instance (export "" (global f32)))))
"instance types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "i" (instance (export "x" (func)))))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 funcref)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 2)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 1 10)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "max" (memory 2 10)))))
"module types incompatible")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module)))))
"module types incompatible")

38
tests/misc_testsuite/module-linking/instantiate.wast

@ -117,6 +117,24 @@
)
(assert_return (invoke "get") (i32.const 5))
;; imported modules again
(module
(module $m
(import "" (module $m (export "get" (func (result i32)))))
(instance $i (instantiate $m))
(alias $f (instance $i) (func 0))
(export "" (func $f))
)
(module $m2
(func (export "get") (result i32)
i32.const 6))
(instance $a (instantiate $m (module $m2)))
(func (export "get") (result i32)
call $a.$f)
)
(assert_return (invoke "get") (i32.const 6))
;; all at once
(module
(import "a" "inc" (func $f))
@ -195,3 +213,23 @@
(instance (instantiate 0 (func 0)))
)
(assert_return (invoke $a "get") (i32.const 1))
;; module/instance top-level imports work
(module $b
(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")
(assert_unlinkable
(module
(import "b" "i" (instance (export "" (func))))
)
"instance types incompatible")

Loading…
Cancel
Save