use super::{super::REALLOC_AND_FREE, engine}; use anyhow::Result; use wasmtime::{ component::{Component, Linker}, Store, }; fn component() -> String { format!( r#" (component (core module $libc (memory (export "memory") 1) {REALLOC_AND_FREE} ) (core instance $libc (instantiate $libc)) (core module $m (import "libc" "memory" (memory 0)) (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) (func (export "core_foo_export") (param i32 i32) (result i32) (local $retptr i32) (local.set $retptr (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) (i32.store offset=0 (local.get $retptr) (local.get 0)) (i32.store offset=4 (local.get $retptr) (local.get 1)) (local.get $retptr) ) (func (export "core_bar_export") (param i32 i32 i32 i32)) (func (export "core_baz_export") (param i32 i32 i32 i32) (result i32) (local $retptr i32) (local.set $retptr (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 16))) (i32.store offset=0 (local.get $retptr) (local.get 0)) (i32.store offset=4 (local.get $retptr) (local.get 1)) (i32.store offset=8 (local.get $retptr) (local.get 2)) (i32.store offset=12 (local.get $retptr) (local.get 3)) (local.get $retptr) ) ) (core instance $i (instantiate $m (with "libc" (instance $libc)) )) (func $f_foo (param "a" (list (list string))) (result (list (list string))) (canon lift (core func $i "core_foo_export") (memory $libc "memory") (realloc (func $libc "realloc")) ) ) (type $thing (record (field "name" string) (field "value" (list string)))) (func $f_bar (param "a" $thing) (canon lift (core func $i "core_bar_export") (memory $libc "memory") (realloc (func $libc "realloc")) ) ) (func $f_baz (param "a" $thing) (result $thing) (canon lift (core func $i "core_baz_export") (memory $libc "memory") (realloc (func $libc "realloc")) ) ) (component $c_lists (import "import-foo" (func $f (param "a" (list (list string))) (result (list (list string))) )) (export "foo" (func $f)) ) (instance $i_lists (instantiate $c_lists (with "import-foo" (func $f_foo)) )) (export "lists" (instance $i_lists)) (component $c_thing_in (import "import-thing" (type $import-thing (eq $thing))) (import "import-bar" (func $f (param "a" $import-thing))) (export $export-thing "thing" (type $thing)) (export "bar" (func $f) (func (param "a" $export-thing))) ) (instance $i_thing_in (instantiate $c_thing_in (with "import-thing" (type $thing)) (with "import-bar" (func $f_bar)) )) (export "thing-in" (instance $i_thing_in)) (component $c_thing_in_and_out (import "import-thing" (type $import-thing (eq $thing))) (import "import-baz" (func $f (param "a" $import-thing) (result $import-thing))) (export $export-thing "thing" (type $thing)) (export "baz" (func $f) (func (param "a" $export-thing) (result $export-thing))) ) (instance $i_thing_in_and_out (instantiate $c_thing_in_and_out (with "import-thing" (type $thing)) (with "import-baz" (func $f_baz)) )) (export "thing-in-and-out" (instance $i_thing_in_and_out)) ) "# ) } #[test] fn owning() -> Result<()> { wasmtime::component::bindgen!({ inline: " package inline:inline world test { export lists: interface { foo: func(a: list>) -> list> } export thing-in: interface { record thing { name: string, value: list } bar: func(a: thing) } export thing-in-and-out: interface { record thing { name: string, value: list } baz: func(a: thing) -> thing } }", ownership: Owning }); impl PartialEq for exports::thing_in::Thing { fn eq(&self, other: &Self) -> bool { self.name == other.name && self.value == other.value } } impl PartialEq for exports::thing_in_and_out::Thing { fn eq(&self, other: &Self) -> bool { self.name == other.name && self.value == other.value } } let engine = engine(); let component = Component::new(&engine, component())?; let linker = Linker::new(&engine); let mut store = Store::new(&engine, ()); let (test, _) = Test::instantiate(&mut store, &component, &linker)?; let value = vec![vec!["a".to_owned(), "b".to_owned()]]; assert_eq!(value, test.lists().call_foo(&mut store, &value)?); let value = exports::thing_in::Thing { name: "thing 1".to_owned(), value: vec!["some value".to_owned(), "another value".to_owned()], }; test.thing_in().call_bar(&mut store, &value)?; let value = exports::thing_in_and_out::Thing { name: "thing 1".to_owned(), value: vec!["some value".to_owned(), "another value".to_owned()], }; assert_eq!(value, test.thing_in_and_out().call_baz(&mut store, &value)?); Ok(()) } #[test] fn borrowing_no_duplication() -> Result<()> { wasmtime::component::bindgen!({ inline: " package inline:inline world test { export lists: interface { foo: func(a: list>) -> list> } export thing-in: interface { record thing { name: string, value: list } bar: func(a: thing) } export thing-in-and-out: interface { record thing { name: string, value: list } baz: func(a: thing) -> thing } }", ownership: Borrowing { duplicate_if_necessary: false } }); impl PartialEq for exports::thing_in::Thing<'_> { fn eq(&self, other: &Self) -> bool { self.name == other.name && self.value == other.value } } impl PartialEq for exports::thing_in_and_out::Thing { fn eq(&self, other: &Self) -> bool { self.name == other.name && self.value == other.value } } let engine = engine(); let component = Component::new(&engine, component())?; let linker = Linker::new(&engine); let mut store = Store::new(&engine, ()); let (test, _) = Test::instantiate(&mut store, &component, &linker)?; let value = &[&["a", "b"] as &[_]] as &[_]; assert_eq!(value, test.lists().call_foo(&mut store, value)?); let value = exports::thing_in::Thing { name: "thing 1", value: &["some value", "another value"], }; test.thing_in().call_bar(&mut store, value)?; let value = exports::thing_in_and_out::Thing { name: "thing 1".to_owned(), value: vec!["some value".to_owned(), "another value".to_owned()], }; assert_eq!(value, test.thing_in_and_out().call_baz(&mut store, &value)?); Ok(()) } #[test] fn borrowing_with_duplication() -> Result<()> { wasmtime::component::bindgen!({ inline: " package inline:inline world test { export lists: interface { foo: func(a: list>) -> list> } export thing-in: interface { record thing { name: string, value: list } bar: func(a: thing) } export thing-in-and-out: interface { record thing { name: string, value: list } baz: func(a: thing) -> thing } }", ownership: Borrowing { duplicate_if_necessary: true } }); impl PartialEq for exports::thing_in::Thing<'_> { fn eq(&self, other: &Self) -> bool { self.name == other.name && self.value == other.value } } impl PartialEq for exports::thing_in_and_out::ThingResult { fn eq(&self, other: &Self) -> bool { self.name == other.name && self.value == other.value } } let engine = engine(); let component = Component::new(&engine, component())?; let linker = Linker::new(&engine); let mut store = Store::new(&engine, ()); let (test, _) = Test::instantiate(&mut store, &component, &linker)?; let value = &[&["a", "b"] as &[_]] as &[_]; assert_eq!(value, test.lists().call_foo(&mut store, value)?); let value = exports::thing_in::Thing { name: "thing 1", value: &["some value", "another value"], }; test.thing_in().call_bar(&mut store, value)?; let value = exports::thing_in_and_out::ThingParam { name: "thing 1", value: &["some value", "another value"], }; assert_eq!( exports::thing_in_and_out::ThingResult { name: "thing 1".to_owned(), value: vec!["some value".to_owned(), "another value".to_owned()], }, test.thing_in_and_out().call_baz(&mut store, value)? ); Ok(()) }