| use { |
| anyhow::{Context, Result}, |
| wit_component::StringEncoding, |
| wit_parser::Resolve, |
| }; |
| |
| const FOO: &str = r#" |
| (module |
| (@dylink.0 |
| (mem-info (memory 4 4)) |
| (needed "libc.so") |
| ) |
| (type (func)) |
| (type (func (param i32) (result i32))) |
| (import "env" "memory" (memory 1)) |
| (import "env" "__indirect_function_table" (table 0 funcref)) |
| (import "env" "__stack_pointer" (global $__stack_pointer (mut i32))) |
| (import "env" "__memory_base" (global $__memory_base i32)) |
| (import "env" "__table_base" (global $__table_base i32)) |
| (import "env" "malloc" (func $malloc (type 1))) |
| (import "env" "abort" (func $abort (type 0))) |
| (import "GOT.mem" "um" (global $um (mut i32))) |
| (import "test:test/test" "bar" (func $bar (type 1))) |
| (func $__wasm_call_ctors (type 0)) |
| (func $__wasm_apply_data_relocs (type 0)) |
| (func $foo (type 1) (param i32) (result i32) |
| global.get $__stack_pointer |
| i32.const 16 |
| i32.sub |
| global.set $__stack_pointer |
| |
| i32.const 4 |
| call $malloc |
| |
| i32.const 0 |
| i32.eq |
| if |
| call $abort |
| unreachable |
| end |
| |
| local.get 0 |
| global.get $um |
| i32.load offset=16 |
| i32.add |
| i32.const 42 |
| i32.add |
| |
| call $bar |
| |
| global.get $__stack_pointer |
| i32.const 16 |
| i32.add |
| global.set $__stack_pointer |
| ) |
| (global i32 i32.const 0) |
| (export "__wasm_call_ctors" (func $__wasm_call_ctors)) |
| (export "__wasm_apply_data_relocs" (func $__wasm_apply_data_relocs)) |
| (export "foo" (func $foo)) |
| (export "well" (global 4)) |
| (data $.data (global.get $__memory_base) "\04\00\00\00") |
| ) |
| "#; |
| |
| const BAR: &str = r#" |
| (module |
| (@dylink.0 |
| (mem-info (memory 20 4)) |
| (needed "libfoo.so") |
| ) |
| (type (func (param i32) (result i32))) |
| (type (func)) |
| (import "env" "memory" (memory 1)) |
| (import "env" "__indirect_function_table" (table 0 funcref)) |
| (import "env" "__memory_base" (global $__memory_base i32)) |
| (import "env" "__table_base" (global $__table_base i32)) |
| (import "env" "foo" (func $foo (type 0))) |
| (import "GOT.mem" "well" (global $well (mut i32))) |
| (func $__wasm_call_ctors (type 1)) |
| (func $__wasm_apply_data_relocs (type 1)) |
| (func $bar (type 0) (param i32) (result i32) |
| local.get 0 |
| call $foo |
| global.get $well |
| i32.load |
| i32.add |
| ) |
| (global i32 i32.const 0) |
| (export "__wasm_call_ctors" (func $__wasm_call_ctors)) |
| (export "__wasm_apply_data_relocs" (func $__wasm_apply_data_relocs)) |
| (export "test:test/test#bar" (func $bar)) |
| (export "um" (global 3)) |
| (data $.data (global.get $__memory_base) "\01\00\00\00\02\00\00\00\03\00\00\00\04\00\00\00\05\00\00\00") |
| ) |
| "#; |
| |
| const LIBC: &str = r#" |
| (module |
| (@dylink.0) |
| (type (func)) |
| (type (func (param i32) (result i32))) |
| (import "GOT.mem" "__heap_base" (global $__heap_base (mut i32))) |
| (import "GOT.mem" "__heap_end" (global $__heap_end (mut i32))) |
| (global $heap (mut i32) i32.const 0) |
| (func $start (type 0) |
| global.get $__heap_base |
| global.set $heap |
| ) |
| (func $malloc (type 1) (param i32) (result i32) |
| global.get $heap |
| global.get $heap |
| local.get 0 |
| i32.add |
| global.set $heap |
| ) |
| (func $abort (type 0) |
| unreachable |
| ) |
| (export "malloc" (func $malloc)) |
| (export "abort" (func $abort)) |
| (start $start) |
| ) |
| "#; |
| |
| const WIT: &str = r#" |
| package test:test; |
| |
| interface test { |
| bar: func(v: s32) -> s32; |
| } |
| |
| world bar { |
| import test; |
| export test; |
| } |
| "#; |
| |
| fn encode(wat: &str, wit: Option<&str>) -> Result<Vec<u8>> { |
| let mut module = wat::parse_str(wat)?; |
| |
| if let Some(wit) = wit { |
| let mut resolve = Resolve::default(); |
| let pkg = resolve.push_str("test.wit", wit)?; |
| let world = resolve.select_world(pkg, None)?; |
| |
| wit_component::embed_component_metadata( |
| &mut module, |
| &resolve, |
| world, |
| StringEncoding::UTF8, |
| )?; |
| } |
| |
| wasmparser::validate(&module)?; |
| |
| Ok(module) |
| } |
| |
| #[test] |
| fn linking() -> Result<()> { |
| let component = [ |
| ("libfoo.so", FOO, None), |
| ("libbar.so", BAR, Some(WIT)), |
| ("libc.so", LIBC, None), |
| ] |
| .into_iter() |
| .try_fold( |
| wit_component::Linker::default().validate(true), |
| |linker, (name, wat, wit)| { |
| linker.library( |
| name, |
| &encode(wat, wit).with_context(|| name.to_owned())?, |
| false, |
| ) |
| }, |
| )? |
| .encode()?; |
| |
| #[cfg(target_family = "wasm")] |
| { |
| _ = component; |
| } |
| |
| #[cfg(not(target_family = "wasm"))] |
| { |
| use { |
| anyhow::anyhow, |
| wasmtime::{ |
| component::{Component, Linker}, |
| Config, Engine, Store, |
| }, |
| }; |
| |
| let mut config = Config::new(); |
| config.wasm_component_model(true); |
| |
| let engine = Engine::new(&config)?; |
| let mut linker = Linker::new(&engine); |
| linker |
| .instance("test:test/test")? |
| .func_wrap("bar", |_store, (v,): (i32,)| Ok((v + 7,)))?; |
| let mut store = Store::new(&engine, ()); |
| let instance = linker.instantiate(&mut store, &Component::new(&engine, &component)?)?; |
| let func = instance |
| .get_export(&mut store, None, "test:test/test") |
| .and_then(|i| instance.get_export(&mut store, Some(&i), "bar")) |
| .and_then(|f| { |
| instance |
| .get_typed_func::<(i32,), (i32,)>(&mut store, &f) |
| .ok() |
| }) |
| .ok_or_else(|| anyhow!("func `test:test/test/bar` not found"))?; |
| |
| assert_eq!(65, func.call(&mut store, (7,))?.0); |
| } |
| |
| Ok(()) |
| } |