Remove the module linking implementation in Wasmtime (#3958)

* Remove the module linking implementation in Wasmtime

This commit removes the experimental implementation of the module
linking WebAssembly proposal from Wasmtime. The module linking is no
longer intended for core WebAssembly but is instead incorporated into
the component model now at this point. This means that very large parts
of Wasmtime's implementation of module linking are no longer applicable
and would change greatly with an implementation of the component model.

The main purpose of this is to remove Wasmtime's reliance on the support
for module-linking in `wasmparser` and tooling crates. With this
reliance removed we can move over to the `component-model` branch of
`wasmparser` and use the updated support for the component model.
Additionally given the trajectory of the component model proposal the
embedding API of Wasmtime will not look like what it looks like today
for WebAssembly. For example the core wasm `Instance` will not change
and instead a `Component` is likely to be added instead.

Some more rationale for this is in #3941, but the basic idea is that I
feel that it's not going to be viable to develop support for the
component model on a non-`main` branch of Wasmtime. Additionaly I don't
think it's viable, for the same reasons as `wasm-tools`, to support the
old module linking proposal and the new component model at the same
time.

This commit takes a moment to not only delete the existing module
linking implementation but some abstractions are also simplified. For
example module serialization is a bit simpler that there's only one
module. Additionally instantiation is much simpler since the only
initializer we have to deal with are imports and nothing else.

Closes #3941

* Fix doc link

* Update comments
This commit is contained in:
Alex Crichton
2022-03-23 14:57:34 -05:00
committed by GitHub
parent 6a60e8363f
commit 76b82910c9
51 changed files with 400 additions and 4555 deletions

View File

@@ -50,7 +50,7 @@ fn smoke_host_func() -> Result<()> {
linker.func_wrap0_async("", "second", move |_caller| Box::new(async { Ok(()) }))?;
let func = linker
.get(&mut store, "", Some("first"))
.get(&mut store, "", "first")
.unwrap()
.into_func()
.unwrap();
@@ -58,7 +58,7 @@ fn smoke_host_func() -> Result<()> {
run_smoke_typed_test(&mut store, func);
let func = linker
.get(&mut store, "", Some("second"))
.get(&mut store, "", "second")
.unwrap()
.into_func()
.unwrap();
@@ -119,7 +119,7 @@ fn smoke_host_func_with_suspension() -> Result<()> {
})?;
let func = linker
.get(&mut store, "", Some("first"))
.get(&mut store, "", "first")
.unwrap()
.into_func()
.unwrap();
@@ -127,7 +127,7 @@ fn smoke_host_func_with_suspension() -> Result<()> {
run_smoke_typed_test(&mut store, func);
let func = linker
.get(&mut store, "", Some("second"))
.get(&mut store, "", "second")
.unwrap()
.into_func()
.unwrap();
@@ -474,11 +474,7 @@ fn async_host_func_with_pooling_stacks() -> Result<()> {
move |_caller, _params, _results| Box::new(async { Ok(()) }),
)?;
let func = linker
.get(&mut store, "", Some(""))
.unwrap()
.into_func()
.unwrap();
let func = linker.get(&mut store, "", "").unwrap().into_func().unwrap();
run_smoke_test(&mut store, func);
run_smoke_typed_test(&mut store, func);
Ok(())

View File

@@ -106,7 +106,7 @@ mod tests {
module
.imports()
.map(|import| {
assert_eq!(Some("hostcall_read"), import.name());
assert_eq!("hostcall_read", import.name());
let func = Func::wrap(&mut *store, {
move |mut caller: Caller<'_, _>| {
let mem = caller.get_export("memory").unwrap().into_memory().unwrap();

View File

@@ -104,7 +104,7 @@ fn drop_delayed() -> Result<()> {
let module = Module::new(&engine, &wat::parse_str(r#"(import "" "" (func))"#)?)?;
let mut store = Store::new(&engine, ());
let func = linker.get(&mut store, "", Some("")).unwrap();
let func = linker.get(&mut store, "", "").unwrap();
Instance::new(&mut store, &module, &[func])?;
drop(store);
@@ -112,7 +112,7 @@ fn drop_delayed() -> Result<()> {
assert_eq!(HITS.load(SeqCst), 0);
let mut store = Store::new(&engine, ());
let func = linker.get(&mut store, "", Some("")).unwrap();
let func = linker.get(&mut store, "", "").unwrap();
Instance::new(&mut store, &module, &[func])?;
drop(store);
@@ -147,7 +147,7 @@ fn signatures_match() -> Result<()> {
let mut store = Store::new(&engine, ());
let f = linker
.get(&mut store, "", Some("f1"))
.get(&mut store, "", "f1")
.unwrap()
.into_func()
.unwrap();
@@ -155,7 +155,7 @@ fn signatures_match() -> Result<()> {
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[]);
let f = linker
.get(&mut store, "", Some("f2"))
.get(&mut store, "", "f2")
.unwrap()
.into_func()
.unwrap();
@@ -163,7 +163,7 @@ fn signatures_match() -> Result<()> {
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::I32]);
let f = linker
.get(&mut store, "", Some("f3"))
.get(&mut store, "", "f3")
.unwrap()
.into_func()
.unwrap();
@@ -171,7 +171,7 @@ fn signatures_match() -> Result<()> {
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::I64]);
let f = linker
.get(&mut store, "", Some("f4"))
.get(&mut store, "", "f4")
.unwrap()
.into_func()
.unwrap();
@@ -179,7 +179,7 @@ fn signatures_match() -> Result<()> {
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::F32]);
let f = linker
.get(&mut store, "", Some("f5"))
.get(&mut store, "", "f5")
.unwrap()
.into_func()
.unwrap();
@@ -187,7 +187,7 @@ fn signatures_match() -> Result<()> {
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::F64]);
let f = linker
.get(&mut store, "", Some("f6"))
.get(&mut store, "", "f6")
.unwrap()
.into_func()
.unwrap();
@@ -448,11 +448,7 @@ fn trap_smoke() -> Result<()> {
let mut store = Store::new(&engine, ());
let f = linker
.get(&mut store, "", Some(""))
.unwrap()
.into_func()
.unwrap();
let f = linker.get(&mut store, "", "").unwrap().into_func().unwrap();
let err = f
.call(&mut store, &[], &mut [])
@@ -506,7 +502,7 @@ fn new_from_signature() -> Result<()> {
let mut store = Store::new(&engine, ());
let f = linker
.get(&mut store, "", Some("f1"))
.get(&mut store, "", "f1")
.unwrap()
.into_func()
.unwrap();
@@ -515,7 +511,7 @@ fn new_from_signature() -> Result<()> {
assert!(f.typed::<i32, (), _>(&store).is_err());
let f = linker
.get(&mut store, "", Some("f2"))
.get(&mut store, "", "f2")
.unwrap()
.into_func()
.unwrap();
@@ -551,7 +547,7 @@ fn call_wrapped_func() -> Result<()> {
let mut store = Store::new(&engine, ());
let f = linker
.get(&mut store, "", Some("f1"))
.get(&mut store, "", "f1")
.unwrap()
.into_func()
.unwrap();
@@ -564,7 +560,7 @@ fn call_wrapped_func() -> Result<()> {
.call(&mut store, (1, 2, 3.0, 4.0))?;
let f = linker
.get(&mut store, "", Some("f2"))
.get(&mut store, "", "f2")
.unwrap()
.into_func()
.unwrap();
@@ -573,7 +569,7 @@ fn call_wrapped_func() -> Result<()> {
assert_eq!(f.typed::<(), i32, _>(&store)?.call(&mut store, ())?, 1);
let f = linker
.get(&mut store, "", Some("f3"))
.get(&mut store, "", "f3")
.unwrap()
.into_func()
.unwrap();
@@ -582,7 +578,7 @@ fn call_wrapped_func() -> Result<()> {
assert_eq!(f.typed::<(), i64, _>(&store)?.call(&mut store, ())?, 2);
let f = linker
.get(&mut store, "", Some("f4"))
.get(&mut store, "", "f4")
.unwrap()
.into_func()
.unwrap();
@@ -591,7 +587,7 @@ fn call_wrapped_func() -> Result<()> {
assert_eq!(f.typed::<(), f32, _>(&store)?.call(&mut store, ())?, 3.0);
let f = linker
.get(&mut store, "", Some("f5"))
.get(&mut store, "", "f5")
.unwrap()
.into_func()
.unwrap();
@@ -610,11 +606,7 @@ fn func_return_nothing() -> Result<()> {
linker.func_new("", "", ty, |_, _, _| Ok(()))?;
let mut store = Store::new(&engine, ());
let f = linker
.get(&mut store, "", Some(""))
.unwrap()
.into_func()
.unwrap();
let f = linker.get(&mut store, "", "").unwrap().into_func().unwrap();
let err = f
.call(&mut store, &[], &mut [Val::I32(0)])
.unwrap_err()
@@ -661,11 +653,7 @@ fn call_via_funcref() -> Result<()> {
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let f = linker
.get(&mut store, "", Some(""))
.unwrap()
.into_func()
.unwrap();
let f = linker.get(&mut store, "", "").unwrap().into_func().unwrap();
let mut results = [Val::I32(0), Val::I32(0)];
instance
.get_func(&mut store, "call")
@@ -708,11 +696,7 @@ fn store_with_context() -> Result<()> {
let mut store = Store::new(&engine, Ctx { called: false });
let f = linker
.get(&mut store, "", Some(""))
.unwrap()
.into_func()
.unwrap();
let f = linker.get(&mut store, "", "").unwrap().into_func().unwrap();
f.call(&mut store, &[], &mut [])?;
assert!(store.data().called);

View File

@@ -23,34 +23,6 @@ fn link_undefined() -> Result<()> {
Ok(())
}
#[test]
fn undefined_error_message_with_linking() {
let mut config = Config::new();
config.wasm_module_linking(true);
let engine = Engine::new(&config).unwrap();
let module = Module::new(
&engine,
r#"
(module
(import "foo" "bar" (func))
)
"#,
)
.unwrap();
let linker = Linker::new(&engine);
let mut store = Store::new(&engine, ());
assert!(linker
.instantiate(&mut store, &module)
.unwrap_err()
.to_string()
.contains("foo"));
assert!(linker
.instantiate(&mut store, &module)
.unwrap_err()
.to_string()
.contains("bar"));
}
#[test]
fn link_twice_bad() -> Result<()> {
let mut store = Store::<()>::default();
@@ -312,7 +284,7 @@ fn funcs_live_on_to_fight_another_day() -> Result<()> {
let get_and_call = || -> Result<()> {
assert_eq!(flag.load(SeqCst), 0);
let mut store = Store::new(&engine, ());
let func = linker.get(&mut store, "", Some("")).unwrap();
let func = linker.get(&mut store, "", "").unwrap();
func.into_func().unwrap().call(&mut store, &[], &mut [])?;
assert_eq!(flag.load(SeqCst), 0);
Ok(())
@@ -332,8 +304,8 @@ fn alias_one() -> Result<()> {
assert!(linker.alias("a", "b", "c", "d").is_err());
linker.func_wrap("a", "b", || {})?;
assert!(linker.alias("a", "b", "c", "d").is_ok());
assert!(linker.get(&mut store, "a", Some("b")).is_some());
assert!(linker.get(&mut store, "c", Some("d")).is_some());
assert!(linker.get(&mut store, "a", "b").is_some());
assert!(linker.get(&mut store, "c", "d").is_some());
Ok(())
}

View File

@@ -21,7 +21,6 @@ mod linker;
mod memory;
mod memory_creator;
mod module;
mod module_linking;
mod module_serialize;
mod name;
mod pooling_allocator;

View File

@@ -1,300 +0,0 @@
use anyhow::Result;
use wasmtime::*;
fn engine() -> Engine {
let mut config = Config::new();
config.wasm_module_linking(true);
Engine::new(&config).unwrap()
}
#[test]
fn compile() -> Result<()> {
let engine = engine();
Module::new(&engine, "(module (module))")?;
Module::new(&engine, "(module (module) (module))")?;
Module::new(&engine, "(module (module (module)))")?;
Module::new(
&engine,
"
(module
(func)
(module (func))
(module (func))
)
",
)?;
let m = Module::new(
&engine,
"
(module
(global i32 (i32.const 0))
(func)
(module (memory 1) (func))
(module (memory 2) (func))
(module (table 2 funcref) (func))
(module (global i64 (i64.const 0)) (func))
)
",
)?;
assert_eq!(m.imports().len(), 0);
assert_eq!(m.exports().len(), 0);
let bytes = m.serialize()?;
unsafe {
Module::deserialize(&engine, &bytes)?;
}
assert_eq!(m.imports().len(), 0);
assert_eq!(m.exports().len(), 0);
Ok(())
}
#[test]
fn types() -> Result<()> {
let engine = engine();
Module::new(&engine, "(module (type (module)))")?;
Module::new(&engine, "(module (type (instance)))")?;
Ok(())
}
#[test]
fn imports_exports() -> Result<()> {
let engine = engine();
// empty module type
let module = Module::new(&engine, "(module (module (export \"\")))")?;
let mut e = module.exports();
assert_eq!(e.len(), 1);
let export = e.next().unwrap();
assert_eq!(export.name(), "");
let module_ty = match export.ty() {
ExternType::Module(m) => m,
_ => panic!("unexpected type"),
};
assert_eq!(module_ty.imports().len(), 0);
assert_eq!(module_ty.exports().len(), 0);
// empty instance type
let module = Module::new(
&engine,
"
(module
(module)
(instance (export \"\") (instantiate 0)))
",
)?;
let mut e = module.exports();
assert_eq!(e.len(), 1);
let export = e.next().unwrap();
assert_eq!(export.name(), "");
let instance_ty = match export.ty() {
ExternType::Instance(i) => i,
_ => panic!("unexpected type"),
};
assert_eq!(instance_ty.exports().len(), 0);
// full module type
let module = Module::new(
&engine,
"
(module
(import \"\" \"a\" (module
(import \"a\" (func))
(export \"\" (global i32))
))
)
",
)?;
let mut i = module.imports();
assert_eq!(i.len(), 1);
let import = i.next().unwrap();
assert_eq!(import.module(), "");
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"),
};
assert_eq!(module_ty.imports().len(), 1);
assert_eq!(module_ty.exports().len(), 1);
let import = module_ty.imports().next().unwrap();
assert_eq!(import.module(), "a");
assert_eq!(import.name(), None);
match import.ty() {
ExternType::Func(f) => {
assert_eq!(f.results().len(), 0);
assert_eq!(f.params().len(), 0);
}
_ => panic!("unexpected type"),
}
let export = module_ty.exports().next().unwrap();
assert_eq!(export.name(), "");
match export.ty() {
ExternType::Global(g) => {
assert_eq!(*g.content(), ValType::I32);
assert_eq!(g.mutability(), Mutability::Const);
}
_ => panic!("unexpected type"),
}
// full instance type
let module = Module::new(
&engine,
"
(module
(import \"\" \"b\" (instance
(export \"m\" (memory 1))
(export \"t\" (table 1 funcref))
))
)
",
)?;
let mut i = module.imports();
assert_eq!(i.len(), 1);
let import = i.next().unwrap();
assert_eq!(import.module(), "");
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"),
};
assert_eq!(instance_ty.exports().len(), 2);
let mem_export = instance_ty.exports().nth(0).unwrap();
assert_eq!(mem_export.name(), "m");
match mem_export.ty() {
ExternType::Memory(m) => {
assert_eq!(m.minimum(), 1);
assert_eq!(m.maximum(), None);
}
_ => panic!("unexpected type"),
}
let table_export = instance_ty.exports().nth(1).unwrap();
assert_eq!(table_export.name(), "t");
match table_export.ty() {
ExternType::Table(t) => {
assert_eq!(t.minimum(), 1);
assert_eq!(t.maximum(), None);
assert_eq!(t.element(), ValType::FuncRef);
}
_ => panic!("unexpected type"),
}
Ok(())
}
#[test]
fn limit_instances() -> Result<()> {
let mut config = Config::new();
config.wasm_module_linking(true);
let engine = Engine::new(&config)?;
let module = Module::new(
&engine,
r#"
(module $PARENT
(module $m0)
(module $m1
(instance (instantiate (module outer $PARENT $m0)))
(instance (instantiate (module outer $PARENT $m0))))
(module $m2
(instance (instantiate (module outer $PARENT $m1)))
(instance (instantiate (module outer $PARENT $m1))))
(module $m3
(instance (instantiate (module outer $PARENT $m2)))
(instance (instantiate (module outer $PARENT $m2))))
(module $m4
(instance (instantiate (module outer $PARENT $m3)))
(instance (instantiate (module outer $PARENT $m3))))
(module $m5
(instance (instantiate (module outer $PARENT $m4)))
(instance (instantiate (module outer $PARENT $m4))))
(instance (instantiate $m5))
)
"#,
)?;
let mut store = Store::new(&engine, StoreLimitsBuilder::new().instances(10).build());
store.limiter(|s| s as &mut dyn ResourceLimiter);
let err = Instance::new(&mut store, &module, &[]).err().unwrap();
assert!(
err.to_string().contains("resource limit exceeded"),
"bad error: {}",
err
);
Ok(())
}
#[test]
fn limit_memories() -> Result<()> {
let mut config = Config::new();
config.wasm_module_linking(true);
config.wasm_multi_memory(true);
let engine = Engine::new(&config)?;
let module = Module::new(
&engine,
r#"
(module
(module $m0
(memory 1 1)
(memory 1 1)
(memory 1 1)
(memory 1 1)
(memory 1 1)
)
(instance (instantiate $m0))
(instance (instantiate $m0))
(instance (instantiate $m0))
(instance (instantiate $m0))
)
"#,
)?;
let mut store = Store::new(&engine, StoreLimitsBuilder::new().memories(10).build());
store.limiter(|s| s as &mut dyn ResourceLimiter);
let err = Instance::new(&mut store, &module, &[]).err().unwrap();
assert!(
err.to_string().contains("resource limit exceeded"),
"bad error: {}",
err
);
Ok(())
}
#[test]
fn limit_tables() -> Result<()> {
let mut config = Config::new();
config.wasm_module_linking(true);
let engine = Engine::new(&config)?;
let module = Module::new(
&engine,
r#"
(module
(module $m0
(table 1 1 funcref)
(table 1 1 funcref)
(table 1 1 funcref)
(table 1 1 funcref)
(table 1 1 funcref)
)
(instance (instantiate $m0))
(instance (instantiate $m0))
(instance (instantiate $m0))
(instance (instantiate $m0))
)
"#,
)?;
let mut store = Store::new(&engine, StoreLimitsBuilder::new().tables(10).build());
store.limiter(|s| s as &mut dyn ResourceLimiter);
let err = Instance::new(&mut store, &module, &[]).err().unwrap();
assert!(
err.to_string().contains("resource limit exceeded"),
"bad error: {}",
err
);
Ok(())
}

View File

@@ -21,13 +21,11 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()>
let simd = feature_found(wast, "simd");
let memory64 = feature_found(wast, "memory64");
let multi_memory = feature_found(wast, "multi-memory");
let module_linking = feature_found(wast, "module-linking");
let threads = feature_found(wast, "threads");
let mut cfg = Config::new();
cfg.wasm_simd(simd)
.wasm_multi_memory(multi_memory || module_linking)
.wasm_module_linking(module_linking)
.wasm_multi_memory(multi_memory)
.wasm_threads(threads)
.wasm_memory64(memory64)
.cranelift_debug_verifier(true);

View File

@@ -1,67 +0,0 @@
(module $a
(module $m1)
(module $b
(module $m2)
(module $c
(instance (instantiate (module outer $a $m1)))
(instance (instantiate (module outer $b $m2)))
)
(instance (instantiate $c))
)
(instance (instantiate $b))
)
(module $a
(module (export "m"))
)
(module $PARENT
(import "a" "m" (module $b))
(module $c
(module $d
(instance (instantiate (module outer $PARENT $b)))
)
(instance (instantiate $d))
)
(instance (instantiate $c))
)
;; Instantiate `$b` here below twice with two different imports. Ensure the
;; exported modules close over the captured state correctly to ensure that we
;; get the right functions.
(module $a
(module $b (export "close_over_imports")
(import "m" (module $m (export "f" (func (result i32)))))
(module (export "m")
(instance $a (instantiate (module outer $b $m)))
(func (export "f") (result i32)
call (func $a "f"))
)
)
)
(module
(import "a" "close_over_imports" (module $m0
(import "m" (module (export "f" (func (result i32)))))
(export "m" (module (export "f" (func (result i32)))))
))
(module $m1
(func (export "f") (result i32)
i32.const 0))
(instance $m_g1 (instantiate $m0 (import "m" (module $m1))))
(instance $g1 (instantiate (module $m_g1 "m")))
(module $m2
(func (export "f") (result i32)
i32.const 1))
(instance $m_g2 (instantiate $m0 (import "m" (module $m2))))
(instance $g2 (instantiate (module $m_g2 "m")))
(func (export "get1") (result i32)
call (func $g1 "f"))
(func (export "get2") (result i32)
call (func $g2 "f"))
)
(assert_return (invoke "get1") (i32.const 0))
(assert_return (invoke "get2") (i32.const 1))

View File

@@ -1,143 +0,0 @@
;; functions
(module
(module $m
(func $foo (export "foo") (result i32)
i32.const 1)
)
(instance $a (instantiate $m))
(func (export "get") (result i32)
call (func $a "foo"))
)
(assert_return (invoke "get") (i32.const 1))
;; globals
(module
(module $m
(global $g (export "g") (mut i32) (i32.const 2))
)
(instance $a (instantiate $m))
(func (export "get") (result i32)
global.get (global $a "g"))
)
(assert_return (invoke "get") (i32.const 2))
;; memories
(module
(module $m
(memory $m (export "m") 1)
(data (i32.const 0) "\03\00\00\00")
)
(instance $a (instantiate $m))
(alias $a "m" (memory $m))
(func (export "get") (result i32)
i32.const 0
i32.load)
)
(assert_return (invoke "get") (i32.const 3))
;; tables
(module
(module $m
(table $t (export "t") 1 funcref)
(func (result i32)
i32.const 4)
(elem (i32.const 0) 0)
)
(instance $a (instantiate $m))
(func (export "get") (result i32)
i32.const 0
call_indirect (table $a "t") (result i32))
)
(assert_return (invoke "get") (i32.const 4))
;; modules
(module
(module $m
(module $sub (export "module")
(func $f (export "") (result i32)
i32.const 5))
)
(instance $a (instantiate $m))
(instance $b (instantiate (module $a "module")))
(func (export "get") (result i32)
call (func $b ""))
)
(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))
(func (export "get") (result i32)
call (func $a "" ""))
)
(assert_return (invoke "get") (i32.const 6))
;; alias parent -- type
(module
(type $t (func))
(module $m
(func $f (type outer 0 $t))
)
(instance $a (instantiate $m))
)
;; alias outer -- module
(module
(module $a)
(module $m
(instance (instantiate (module outer 0 $a)))
)
(instance (instantiate $m))
)
;; The alias, import, type, module, and instance sections can all be interleaved
(module $ROOT
(module $a)
(type $t (func))
(module $m
;; alias
(alias outer 0 $t (type $thunk))
;; import
(import "" "" (func (type $thunk)))
;; module (referencing parent type)
(module
(func (type outer $m $thunk))
(func (type outer $ROOT $t))
)
;; type
(type $thunk2 (func))
;; module (referencing previous alias)
(module $m2
(func (export "") (type outer $m $thunk2))
)
;; instance
(instance $i (instantiate $m2))
;; alias that instance
(alias $i "" (func $my_f))
;; module
(module $m3
(import "" (func)))
;; use our aliased function to create the module
(instance $i2 (instantiate $m3 (import "" (func $my_f))))
;; module
(module $m4
(import "" (func)))
)
;; instantiate the above module
(module $smol (func $f (export "")))
(instance $smol (instantiate $smol))
(instance (instantiate $m (import "" (instance $smol))))
)

View File

@@ -1,431 +0,0 @@
;; 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))
)
(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))
(export "" (func))
))
)
;; functions
(module $a
(module (export "m")
(func (export ""))))
(module (import "a" "m" (module)))
(module (import "a" "m" (module (export "" (func)))))
(assert_unlinkable
(module (import "a" "m" (module (export "" (func (param i32))))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func (result i32))))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (global i32)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 funcref)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 1)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (instance)))))
"incompatible import type for `a`")
(module $a
(module (export "m")
(global (export "") i32 (i32.const 0))))
;; globals
(module (import "a" "m" (module)))
(module (import "a" "m" (module (export "" (global i32)))))
(assert_unlinkable
(module
(import "a" "m" (module (export "" (global (mut i32)))))
)
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (global f32)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 funcref)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 1)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (instance)))))
"incompatible import type for `a`")
;; tables
(module $a
(module (export "m")
(table (export "") 1 funcref)
(table (export "max") 1 10 funcref)
)
)
(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)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 2 funcref)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 10 funcref)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "max" (table 2 10 funcref)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "max" (table 1 9 funcref)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 1)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (instance)))))
"incompatible import type for `a`")
;; memories
(module $a
(module (export "m")
(memory (export "") 1)
(memory (export "max") 1 10)
)
)
(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)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 funcref)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 2)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 1 10)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "max" (memory 2 10)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "max" (memory 2)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (instance)))))
"incompatible import type for `a`")
;; 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 "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))
))))
)
(module
(import "a" "m" (module (export "d" (module
(import "" (func))
(import "a" (func))
))))
)
(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)))))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "d" (module)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "d" (module (import "" (module)))))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (global f32)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 funcref)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 2)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module (export "foo" (func)))))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (instance)))))
"incompatible import type for `a`")
;; 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))
)
(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)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "i" (instance (export "x" (func)))))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (func)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (table 1 funcref)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 2)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (memory 1 10)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "max" (memory 2 10)))))
"incompatible import type for `a`")
(assert_unlinkable
(module (import "a" "m" (module (export "" (module)))))
"incompatible import type for `a`")

View File

@@ -1,308 +0,0 @@
(module
(module)
(instance $a (instantiate 0))
)
(module $a
(global (export "global") (mut i32) (i32.const 0))
(func (export "reset")
i32.const 0
global.set 0)
(func $set (export "inc")
i32.const 1
global.get 0
i32.add
global.set 0)
(func (export "get") (result i32)
global.get 0)
(func (export "load") (result i32)
i32.const 0
i32.load)
(memory (export "memory") 1)
(table (export "table") 1 funcref)
(elem (i32.const 0) $set)
)
;; Imported functions work
(module
(import "a" "inc" (func $set))
(module
(import "" (func))
(start 0))
(instance $a (instantiate 0 (import "" (func $set))))
)
(assert_return (invoke $a "get") (i32.const 1))
;; Imported globals work
(module
(import "a" "global" (global $g (mut i32)))
(module
(import "" (global (mut i32)))
(func
i32.const 2
global.set 0)
(start 0))
(instance $a (instantiate 0 (import "" (global $g))))
)
(assert_return (invoke $a "get") (i32.const 2))
;; Imported tables work
(module
(import "a" "table" (table $t 1 funcref))
(module
(import "" (table 1 funcref))
(func
i32.const 0
call_indirect)
(start 0))
(instance $a (instantiate 0 (import "" (table $t))))
)
(assert_return (invoke $a "get") (i32.const 3))
;; Imported memories work
(module
(import "a" "memory" (memory $m 1))
(module
(import "" (memory 1))
(func
i32.const 0
i32.const 100
i32.store)
(start 0))
(instance $a (instantiate 0 (import "" (memory $m))))
)
(assert_return (invoke $a "load") (i32.const 100))
;; Imported instances work
(module
(import "a" "inc" (func $set))
(module $m1
(import "" (instance (export "" (func))))
(alias 0 "" (func))
(start 0))
(module $m2
(func (export "") (import "")))
(instance $i (instantiate $m2 (import "" (func $set))))
(instance (instantiate $m1 (import "" (instance $i))))
)
(assert_return (invoke $a "get") (i32.const 4))
;; Imported modules work
(module
(import "a" "inc" (func $set))
(module $m1
(import "" (module $m (export "" (func $f (result i32)))))
(instance $i (instantiate $m))
(func $get (export "") (result i32)
call (func $i "")))
(module $m2
(func (export "") (result i32)
i32.const 5))
(instance $i (instantiate $m1 (import "" (module $m2))))
(func (export "get") (result i32)
call (func $i ""))
)
(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 $i "get" (func $f))
(export "" (func $f))
)
(module $m2
(func (export "get") (result i32)
i32.const 6))
(instance $a (instantiate $m (import "" (module $m2))))
(func (export "get") (result i32)
call (func $a ""))
)
(assert_return (invoke "get") (i32.const 6))
;; all at once
(module
(import "a" "inc" (func $f))
(import "a" "global" (global $g (mut i32)))
(import "a" "table" (table $t 1 funcref))
(import "a" "memory" (memory $m 1))
(module
(import "m" (memory 1))
(import "g" (global (mut i32)))
(import "t" (table 1 funcref))
(import "f" (func))
(func $start
call 0
i32.const 0
i32.const 4
i32.store
i32.const 0
call_indirect
global.get 0
global.set 0)
(start $start))
(instance $a
(instantiate 0
(import "m" (memory $m))
(import "g" (global $g))
(import "t" (table $t))
(import "f" (func $f))
)
)
)
;; instantiate lots
(module
(import "a" "inc" (func $f))
(import "a" "global" (global $g (mut i32)))
(import "a" "table" (table $t 1 funcref))
(import "a" "memory" (memory $m 1))
(module $mm (import "" (memory 1)))
(module $mf (import "" (func)))
(module $mt (import "" (table 1 funcref)))
(module $mg (import "" (global (mut i32))))
(instance (instantiate $mm (import "" (memory $m))))
(instance (instantiate $mf (import "" (func $f))))
(instance (instantiate $mt (import "" (table $t))))
(instance (instantiate $mg (import "" (global $g))))
)
;; instantiate nested
(assert_return (invoke $a "reset"))
(assert_return (invoke $a "get") (i32.const 0))
(module
(import "a" "inc" (func))
(module
(import "" (func))
(module
(import "" (func))
(module
(import "" (func))
(module
(import "" (func))
(start 0)
)
(instance (instantiate 0 (import "" (func 0))))
)
(instance (instantiate 0 (import "" (func 0))))
)
(instance (instantiate 0 (import "" (func 0))))
)
(instance (instantiate 0 (import "" (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)))
(module (import "b" "m" (module (import "" (func)))))
(module (import "b" "i" (instance)))
(assert_unlinkable
(module
(import "b" "i" (instance (export "" (func))))
)
"incompatible import type")
;; ensure we ignore other exported items
(module $b
(module $m
(func (export "f") (result i32)
i32.const 300)
(global (export "g") i32 (i32.const 0xfeed))
)
(instance (export "i") (instantiate 0))
)
(module
(import "b" "i" (instance $i
(export "g" (global $g i32))
))
(func (export "get") (result i32)
global.get (global $i "g"))
)
(assert_return (invoke "get") (i32.const 0xfeed))
;; ensure the right export is used even when subtyping comes into play
(module $b
(module $m
(func (export "f") (result i32)
i32.const 300)
(func (export "g") (param i32) (result i32)
i32.const 100
local.get 0
i32.add)
)
(instance (export "i") (instantiate 0))
)
(module
(import "b" "i" (instance $i
;; notice that this order is swapped
(export "g" (func (param i32) (result i32)))
(export "f" (func (result i32)))
))
(func (export "f") (result i32)
call (func $i "f"))
(func (export "g") (param i32) (result i32)
local.get 0
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 (import "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 (import "" (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 (import "m" (module $m1))))
)