feat: add a histrical wit-bindgen

This commit is contained in:
2023-01-01 00:25:48 +08:00
parent 01e8f5a959
commit aa50d63aec
419 changed files with 45283 additions and 1 deletions

View File

@@ -0,0 +1,72 @@
# Testing wit-bindgen - `codegen
Any tests placed into the `tests/codegen` directory should be raw `*.wit`
files. These files will be executed in all code generators by default most
likely, and the purpose of these files is to execute language-specific
validation for each bindings generator. Basically if there's a bug where
something generates invalid code then this is probably where the test should go.
Note that this directory can have whatever it wants since nothing implements the
interfaces or tries to call them.
# Testing wit-bindgen - `runtime`
Otherwise tests are organized in `tests/runtime/*`. Inside this directory is a
directory-per-test. These tests are somewhat heavyweight so you may want to
extend existing tests, but it's also fine to add new tests at any time.
The purpose of this directory is to contain code that's actually compiled to
wasm and executed on hosts. The code compiled-to-wasm can be one of:
* `wasm.rs` - compiled with Rust to WebAssembly
* `wasm.c` - compiled with Clang
Existence of these files indicates that the language should be supported for the
test, and if a file is missing then it's skipped when running other tests. Each
`wasm.*` file is run inside each of the host files:
* `host.rs` - executes wasms with Wasmtime
* `host.js` - executes WebAssembly with node.js
* `host.py` - executes with `wasmtime`'s PyPI package.
Each of these hosts can also be omitted if the host doesn't implement the test
or something like that. Otherwise for each host that exists when the host's
crate generator crate is tested it will run all these tests.
# Testing Layout
If you're adding a test, all you should generally have to do is edit files in
`tests/runtime/*`. If you're adding a new test it *should* be along the lines of
just dropping some files in there, but currently if you're adding a
Rust-compiled-to-wasm binary you'll need to edit
`crates/test-rust-wasm/Cargo.toml` and add a corresponding binary to
`crates/test-rust-wasm/src/bin/*.rs` (in the same manner as the other tests).
Other than this though all other generators should automatically pick up new
tests.
The actual way tests are hooked up looks roughly like:
* All generator crates have a `codegen.rs` and a `runtime.rs` integration test
file (typically defined in the crate's own `tests/*.rs` directory).
* All generator crates depend on `crates/test-helpers`. This crate will walk the
appropriate directory in the top-level `tests/*` directory.
* The `test-helpers` crate will generate appropriate `#[test]` functions to
execute tests. For example the JS generator will run `eslint` or `tsc`. Rust
tests for `codegen` are simply that they compile.
* The `test-helpers` crate also builds wasm files at build time, both the Rust
and C versions. These are then encoded into the generated `#[test]` functions
to get executed when testing.
The general layout is then that if you want to run the JS host tests you run:
```
$ cargo test -p wit-bindgen-gen-js
```
and if you want to run all tests you can execute:
```
$ cargo test --workspace
```
It's all a bit convoluted so feel free to ask questions on Zulip or open an
issue if you're lost.

View File

@@ -0,0 +1,7 @@
async-no-args: async func()
async-args: async func(a: u32, b: string, c: list<string>)
async-results: async func() -> tuple<u32, string, list<string>>
resource async-resource {
frob: async func()
}

View File

@@ -0,0 +1,4 @@
/// A function that accepts a character
take-char: func(x: char)
/// A function that returns a character
return-char: func() -> char

View File

@@ -0,0 +1,28 @@
// hello 🐱 world
kebab-case: func()
record ludicrous-speed {
how-fast-are-you-going: u32,
i-am-going-extremely-slow: u64,
}
foo: func(x: ludicrous-speed)
%function-with-dashes: func()
%function-with-no-weird-characters: func()
apple: func()
apple-pear: func()
apple-pear-grape: func()
garçon: func()
hühnervögel: func()
москва: func()
東-京: func()
garçon-hühnervögel-москва-東-京: func()
a0: func()
%explicit: func()
%explicit-kebab: func()
// Identifiers with the same name as keywords are quoted.
%bool: func()

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,46 @@
flags flag1 {
b0,
}
flags flag2 {
b0, b1,
}
flags flag4 {
b0, b1, b2, b3,
}
flags flag8 {
b0, b1, b2, b3, b4, b5, b6, b7,
}
flags flag16 {
b0, b1, b2, b3, b4, b5, b6, b7,
b8, b9, b10, b11, b12, b13, b14, b15,
}
flags flag32 {
b0, b1, b2, b3, b4, b5, b6, b7,
b8, b9, b10, b11, b12, b13, b14, b15,
b16, b17, b18, b19, b20, b21, b22, b23,
b24, b25, b26, b27, b28, b29, b30, b31,
}
flags flag64 {
b0, b1, b2, b3, b4, b5, b6, b7,
b8, b9, b10, b11, b12, b13, b14, b15,
b16, b17, b18, b19, b20, b21, b22, b23,
b24, b25, b26, b27, b28, b29, b30, b31,
b32, b33, b34, b35, b36, b37, b38, b39,
b40, b41, b42, b43, b44, b45, b46, b47,
b48, b49, b50, b51, b52, b53, b54, b55,
b56, b57, b58, b59, b60, b61, b62, b63,
}
roundtrip-flag1: func(x: flag1) -> flag1
roundtrip-flag2: func(x: flag2) -> flag2
roundtrip-flag4: func(x: flag4) -> flag4
roundtrip-flag8: func(x: flag8) -> flag8
roundtrip-flag16: func(x: flag16) -> flag16
roundtrip-flag32: func(x: flag32) -> flag32
roundtrip-flag64: func(x: flag64) -> flag64

View File

@@ -0,0 +1,4 @@
float32-param: func(x: float32)
float64-param: func(x: float64)
float32-result: func() -> float32
float64-result: func() -> float64

View File

@@ -0,0 +1,31 @@
a1: func(x: u8)
a2: func(x: s8)
a3: func(x: u16)
a4: func(x: s16)
a5: func(x: u32)
a6: func(x: s32)
a7: func(x: u64)
a8: func(x: s64)
a9: func(
p1: u8,
p2: s8,
p3: u16,
p4: s16,
p5: u32,
p6: s32,
p7: u64,
p8: s64,
)
r1: func() -> u8
r2: func() -> s8
r3: func() -> u16
r4: func() -> s16
r5: func() -> u32
r6: func() -> s32
r7: func() -> u64
r8: func() -> s64
pair-ret: func() -> tuple<s64, u8>

View File

@@ -0,0 +1,76 @@
list-u8-param: func(x: list<u8>)
list-u16-param: func(x: list<u16>)
list-u32-param: func(x: list<u32>)
list-u64-param: func(x: list<u64>)
list-s8-param: func(x: list<s8>)
list-s16-param: func(x: list<s16>)
list-s32-param: func(x: list<s32>)
list-s64-param: func(x: list<s64>)
list-float32-param: func(x: list<float32>)
list-float64-param: func(x: list<float64>)
list-u8-ret: func() -> list<u8>
list-u16-ret: func() -> list<u16>
list-u32-ret: func() -> list<u32>
list-u64-ret: func() -> list<u64>
list-s8-ret: func() -> list<s8>
list-s16-ret: func() -> list<s16>
list-s32-ret: func() -> list<s32>
list-s64-ret: func() -> list<s64>
list-float32-ret: func() -> list<float32>
list-float64-ret: func() -> list<float64>
tuple-list: func(x: list<tuple<u8, s8>>) -> list<tuple<s64, u32>>
string-list-arg: func(a: list<string>)
string-list-ret: func() -> list<string>
tuple-string-list: func(x: list<tuple<u8, string>>) -> list<tuple<string, u8>>
string-list: func(x: list<string>) -> list<string>
record some-record {
x: string,
y: other-record,
z: list<other-record>,
c1: u32,
c2: u64,
c3: s32,
c4: s64,
}
record other-record {
a1: u32,
a2: u64,
a3: s32,
a4: s64,
b: string,
c: list<u8>,
}
record-list: func(x: list<some-record>) -> list<other-record>
record-list-reverse: func(x: list<other-record>) -> list<some-record>
variant some-variant {
a(string),
b,
c(u32),
d(list<other-variant>),
}
variant other-variant {
a,
b(u32),
c(string),
}
variant-list: func(x: list<some-variant>) -> list<other-variant>
type load-store-all-sizes = list<tuple<
string,
u8,
s8,
u16,
s16,
u32,
s32,
u64,
s64,
float32,
float64,
char,
>>
load-store-everything: func(a: load-store-all-sizes) -> load-store-all-sizes

View File

@@ -0,0 +1,47 @@
many-args: func(
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
a8: u64,
a9: u64,
a10: u64,
a11: u64,
a12: u64,
a13: u64,
a14: u64,
a15: u64,
a16: u64,
a17: u64,
a18: u64,
a19: u64,
a20: u64,
)
record big-struct {
a1: string,
a2: string,
a3: string,
a4: string,
a5: string,
a6: string,
a7: string,
a8: string,
a9: string,
a10: string,
a11: string,
a12: string,
a13: string,
a14: string,
a15: string,
a16: string,
a17: string,
a18: string,
a19: string,
a20: string,
}
big-argument: func(x: big-struct)

View File

@@ -0,0 +1,52 @@
tuple-arg: func(x: tuple<char, u32>)
tuple-result: func() -> tuple<char, u32>
record empty {}
empty-arg: func(x: empty)
empty-result: func() -> empty
/// A record containing two scalar fields
/// that both have the same type
record scalars {
/// The first field, named a
a: u32,
/// The second field, named b
b: u32,
}
scalar-arg: func(x: scalars)
scalar-result: func() -> scalars
/// A record that is really just flags
/// All of the fields are bool
record really-flags {
a: bool,
b: bool,
c: bool,
d: bool,
e: bool,
f: bool,
g: bool,
h: bool,
i: bool,
}
flags-arg: func(x: really-flags)
flags-result: func() -> really-flags
record aggregates {
a: scalars,
b: u32,
c: empty,
d: string,
e: really-flags,
}
aggregate-arg: func(x: aggregates)
aggregate-result: func() -> aggregates
type tuple-typedef = tuple<s32>
type int-typedef = s32
type tuple-typedef2 = tuple<int-typedef>
typedef-inout: func(e: tuple-typedef2) -> s32

View File

@@ -0,0 +1,11 @@
resource x
acquire-an-x: func() -> x
receive-an-x: func(val: x)
resource y {
static some-constructor: func() -> y
method-on-y: func()
method-with-param: func(x: u32)
method-with-result: func() -> string
}

View File

@@ -0,0 +1,9 @@
f1: func()
f2: func(a: u32)
f3: func(a: u32, b: u32)
f4: func() -> u32
// TODO: reenable this when smw implements this
//f5: func() -> tuple<u32, u32>
//
//f6: func(a: u32, b: u32, c: u32) -> tuple<u32, u32, u32>

View File

@@ -0,0 +1,5 @@
simple-list1: func(l: list<u32>)
simple-list2: func() -> list<u32>
// TODO: reenable this when smw implements this
// simple-list3: func(a: list<u32>, b: list<u32>) -> tuple<list<u32>, list<u32>>
simple-list4: func(l: list<list<u32>>) -> list<list<u32>>

View File

@@ -0,0 +1,6 @@
enum error {
success,
failure,
}
option-test: func() -> expected<option<string>, error>

View File

@@ -0,0 +1 @@
y: func()

View File

@@ -0,0 +1,3 @@
a: func(x: string)
b: func() -> string
c: func(a: string, b: string) -> string

View File

@@ -0,0 +1,57 @@
/// A union of all of the integral types
union all-integers {
/// Bool is equivalent to a 1 bit integer
/// and is treated that way in some languages
bool,
u8, u16, u32, u64,
s8, s16, s32, s64
}
union all-floats {
float32, float64
}
union all-text {
char, string
}
// Returns the same case as the input but with 1 added
add-one-integer: func(num: all-integers) -> all-integers
// Returns the same case as the input but with 1 added
add-one-float: func(num: all-floats) -> all-floats
// Returns the same case as the input but with the first character replaced
replace-first-char: func(text: all-text, letter: char) -> all-text
// Returns the index of the case provided
identify-integer: func(num: all-integers) -> u8
// Returns the index of the case provided
identify-float: func(num: all-floats) -> u8
// Returns the index of the case provided
identify-text: func(text: all-text) -> u8
union duplicated-s32 {
/// The first s32
s32,
/// The second s32
s32,
/// The third s32
s32
}
// Returns the same case as the input but with 1 added
add-one-duplicated: func(num: duplicated-s32) -> duplicated-s32
// Returns the index of the case provided
identify-duplicated: func(num: duplicated-s32) -> u8
/// A type containing numeric types that are distinct in most languages
union distinguishable-num {
/// A Floating Point Number
float64,
/// A Signed Integer
s64
}
// Returns the same case as the input but with 1 added
add-one-distinguishable-num: func(num: distinguishable-num) -> distinguishable-num
// Returns the index of the case provided
identify-distinguishable-num: func(num: distinguishable-num) -> u8

View File

@@ -0,0 +1,135 @@
enum e1 {
a,
}
e1-arg: func(x: e1)
e1-result: func() -> e1
union u1 {
u32,
float32,
}
u1-arg: func(x: u1)
u1-result: func() -> u1
record empty {}
variant v1 {
a,
b(u1),
c(e1),
d(string),
e(empty),
f,
g(u32),
}
v1-arg: func(x: v1)
v1-result: func() -> v1
bool-arg: func(x: bool)
bool-result: func() -> bool
option-arg: func(
a: option<bool>,
b: option<tuple<>>,
c: option<u32>,
d: option<e1>,
e: option<float32>,
f: option<u1>,
g: option<option<bool>>,
)
option-result: func() -> tuple<
option<bool>,
option<tuple<>>,
option<u32>,
option<e1>,
option<float32>,
option<u1>,
option<option<bool>>,
>
variant casts1 {
a(s32),
b(float32),
}
variant casts2 {
a(float64),
b(float32),
}
variant casts3 {
a(float64),
b(u64),
}
variant casts4 {
a(u32),
b(s64),
}
variant casts5 {
a(float32),
b(s64),
}
variant casts6 {
a(tuple<float32, u32>),
b(tuple<u32, u32>),
}
casts: func(
a: casts1,
b: casts2,
c: casts3,
d: casts4,
e: casts5,
f: casts6,
) -> tuple<
casts1,
casts2,
casts3,
casts4,
casts5,
casts6,
>
expected-arg: func(
a: expected<unit, unit>,
b: expected<unit, e1>,
c: expected<e1, unit>,
d: expected<tuple<>, tuple<>>,
e: expected<u32, v1>,
f: expected<string, list<u8>>,
)
expected-result: func() -> tuple<
expected<unit, unit>,
expected<unit, e1>,
expected<e1, unit>,
expected<tuple<>, tuple<>>,
expected<u32, v1>,
expected<string, list<u8>>,
>
enum my-errno {
bad1,
bad2,
}
return-expected-sugar: func() -> expected<s32, my-errno>
return-expected-sugar2: func() -> expected<unit, my-errno>
return-expected-sugar3: func() -> expected<my-errno, my-errno>
return-expected-sugar4: func() -> expected<tuple<s32, u32>, my-errno>
return-option-sugar: func() -> option<s32>
return-option-sugar2: func() -> option<my-errno>
expected-simple: func() -> expected<u32, s32>
record is-clone {
v1: v1,
}
is-clone-arg: func(a: is-clone)
is-clone-return: func() -> is-clone

View File

@@ -0,0 +1,4 @@
thunk: async func()
allocated-bytes: func() -> u32
test-concurrent: async func()

View File

@@ -0,0 +1,106 @@
import { addImportsToImports, Imports } from "./imports.js";
import { Exports } from "./exports.js";
import { getWasm, addWasiToImports } from "./helpers.js";
// @ts-ignore
import * as assert from 'assert';
function promiseChannel(): [Promise<void>, () => void] {
let resolveCallback = null;
const promise = new Promise((resolve, reject) => resolveCallback = resolve);
// @ts-ignore
return [promise, resolveCallback];
}
async function run() {
const importObj = {};
let hit = false;
const [concurrentPromise, resolveConcurrent] = promiseChannel();
const [unblockConcurrent1, resolveUnblockConcurrent1] = promiseChannel();
const [unblockConcurrent2, resolveUnblockConcurrent2] = promiseChannel();
const [unblockConcurrent3, resolveUnblockConcurrent3] = promiseChannel();
const imports: Imports = {
async thunk() {
if (hit) {
console.log('second time in thunk, throwing an error');
throw new Error('catch me');
} else {
console.log('first time in thunk');
await some_helper();
console.log('waited on the helper, returning from host thunk');
hit = true;
}
},
async concurrent1(val) {
console.log('wasm called concurrent1');
assert.equal(val, 1);
resolveUnblockConcurrent1();
console.log('concurrent1 to reenter back into the host');
await concurrentPromise;
console.log('concurrent1 returning to wasm');
return 11;
},
async concurrent2(val) {
console.log('wasm called concurrent2');
assert.equal(val, 2);
resolveUnblockConcurrent2();
console.log('concurrent2 to reenter back into the host');
await concurrentPromise;
console.log('concurrent2 returning to wasm');
return 12;
},
async concurrent3(val) {
console.log('wasm called concurrent3');
assert.equal(val, 3);
resolveUnblockConcurrent3();
console.log('concurrent3 to reenter back into the host');
await concurrentPromise;
console.log('concurrent3 returning to wasm');
return 13;
},
};
let instance: WebAssembly.Instance;
addImportsToImports(importObj, imports, name => instance.exports[name]);
const wasi = addWasiToImports(importObj);
const wasm = new Exports();
await wasm.instantiate(getWasm(), importObj);
wasi.start(wasm.instance);
instance = wasm.instance;
const initBytes = wasm.allocatedBytes();
console.log("calling initial async function");
await wasm.thunk();
assert.ok(hit, "import not called");
assert.equal(initBytes, wasm.allocatedBytes());
// Make sure that exceptions on the host make their way back to whomever's
// doing the actual `await`
try {
console.log('executing thunk export a second time');
await wasm.thunk();
throw new Error('expected an error to get thrown');
} catch (e) {
const err = e as Error;
console.log('caught error with', err.message);
assert.equal(err.message, 'catch me');
}
console.log('entering wasm');
const concurrentWasm = wasm.testConcurrent();
console.log('waiting for wasm to enter the host');
await unblockConcurrent1;
await unblockConcurrent2;
await unblockConcurrent3;
console.log('allowing host functions to finish');
resolveConcurrent();
console.log('waiting on host functions');
await concurrentWasm;
console.log('concurrent wasm finished');
}
async function some_helper() {}
await run()

View File

@@ -0,0 +1,5 @@
thunk: async func()
concurrent1: async func(a: u32) -> u32
concurrent2: async func(a: u32) -> u32
concurrent3: async func(a: u32) -> u32

View File

@@ -0,0 +1,23 @@
wit_bindgen_rust::import!("../../tests/runtime/async_functions/imports.wit");
wit_bindgen_rust::export!("../../tests/runtime/async_functions/exports.wit");
struct Exports;
#[wit_bindgen_rust::async_trait(?Send)]
impl exports::Exports for Exports {
fn allocated_bytes() -> u32 {
test_rust_wasm::get() as u32
}
async fn thunk() {
imports::thunk().await;
}
async fn test_concurrent() {
let a1 = imports::concurrent1(1);
let a2 = imports::concurrent2(2);
let a3 = imports::concurrent3(3);
assert_eq!(futures_util::join!(a2, a3, a1), (12, 13, 11));
}
}

View File

@@ -0,0 +1,31 @@
test-imports: func()
record list-in-record1 { a: string }
record list-in-record2 { a: string }
record list-in-record3 { a: string }
record list-in-record4 { a: string }
type list-in-alias = list-in-record4
list-in-record1: func(a: list-in-record1)
list-in-record2: func() -> list-in-record2
list-in-record3: func(a: list-in-record3) -> list-in-record3
list-in-record4: func(a: list-in-alias) -> list-in-alias
type list-in-variant1-v1 = option<string>
type list-in-variant1-v2 = expected<unit, string>
union list-in-variant1-v3 { string, float32 }
list-in-variant1: func(a: list-in-variant1-v1, b: list-in-variant1-v2, c: list-in-variant1-v3)
type list-in-variant2 = option<string>
list-in-variant2: func() -> list-in-variant2
type list-in-variant3 = option<string>
list-in-variant3: func(a: list-in-variant3) -> list-in-variant3
enum my-errno { success, a, b }
errno-result: func() -> expected<unit, my-errno>
type list-typedef = string
type list-typedef2 = list<u8>
type list-typedef3 = list<string>
list-typedefs: func(a: list-typedef, c: list-typedef3) -> tuple<list-typedef2, list-typedef3>

View File

@@ -0,0 +1,86 @@
from exports.bindings import Exports
from imports.bindings import add_imports_to_linker, Imports
from typing import Tuple, List
import exports.bindings as e
import imports.bindings as i
import sys
import wasmtime
class MyImports:
def list_in_record1(self, a: i.ListInRecord1) -> None:
pass
def list_in_record2(self) -> i.ListInRecord2:
return i.ListInRecord2('list_in_record2')
def list_in_record3(self, a: i.ListInRecord3) -> i.ListInRecord3:
assert(a.a == 'list_in_record3 input')
return i.ListInRecord3('list_in_record3 output')
def list_in_record4(self, a: i.ListInAlias) -> i.ListInAlias:
assert(a.a == 'input4')
return i.ListInRecord4('result4')
def list_in_variant1(self, a: i.ListInVariant1V1, b: i.ListInVariant1V2, c: i.ListInVariant1V3) -> None:
assert(a == 'foo')
assert(b == i.Err('bar'))
assert(c == 'baz')
def list_in_variant2(self) -> i.ListInVariant2:
return 'list_in_variant2'
def list_in_variant3(self, a: i.ListInVariant3) -> i.ListInVariant3:
assert(a == 'input3')
return 'output3'
def errno_result(self) -> i.Expected[None, i.MyErrno]:
return i.Err(i.MyErrno.B)
def list_typedefs(self, a: i.ListTypedef, c: i.ListTypedef3) -> Tuple[i.ListTypedef2, i.ListTypedef3]:
assert(a == 'typedef1')
assert(c == ['typedef2'])
return (b'typedef3', ['typedef4'])
def list_of_variants(self, a: List[bool], b: List[i.Expected[None, None]], c: List[i.MyErrno]) -> Tuple[List[bool], List[i.Expected[None, None]], List[i.MyErrno]]:
assert(a == [True, False])
assert(b == [i.Ok(None), i.Err(None)])
assert(c == [i.MyErrno.SUCCESS, i.MyErrno.A])
return (
[False, True],
[i.Err(None), i.Ok(None)],
[i.MyErrno.A, i.MyErrno.B],
)
def run(wasm_file: str) -> None:
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, wasm_file)
linker = wasmtime.Linker(store.engine)
linker.define_wasi()
wasi = wasmtime.WasiConfig()
wasi.inherit_stdout()
wasi.inherit_stderr()
store.set_wasi(wasi)
imports = MyImports()
add_imports_to_linker(linker, store, imports)
wasm = Exports(store, linker, module)
wasm.test_imports(store)
wasm.list_in_record1(store, e.ListInRecord1("list_in_record1"))
assert(wasm.list_in_record2(store) == e.ListInRecord2(a="list_in_record2"))
assert(wasm.list_in_record3(store, e.ListInRecord3("list_in_record3 input")).a == "list_in_record3 output")
assert(wasm.list_in_record4(store, e.ListInRecord4("input4")).a == "result4")
wasm.list_in_variant1(store, "foo", e.Err("bar"), 'baz')
assert(wasm.list_in_variant2(store) == "list_in_variant2")
assert(wasm.list_in_variant3(store, "input3") == "output3")
assert(isinstance(wasm.errno_result(store), e.Err))
r1, r2 = wasm.list_typedefs(store, "typedef1", ["typedef2"])
assert(r1 == b'typedef3')
assert(r2 == ['typedef4'])
if __name__ == '__main__':
run(sys.argv[1])

View File

@@ -0,0 +1,160 @@
use anyhow::Result;
wit_bindgen_wasmtime::export!("../../tests/runtime/flavorful/imports.wit");
use imports::*;
#[derive(Default)]
pub struct MyImports;
impl Imports for MyImports {
fn list_in_record1(&mut self, ty: ListInRecord1<'_>) {
assert_eq!(ty.a, "list_in_record1");
}
fn list_in_record2(&mut self) -> ListInRecord2 {
ListInRecord2 {
a: "list_in_record2".to_string(),
}
}
fn list_in_record3(&mut self, a: ListInRecord3Param<'_>) -> ListInRecord3Result {
assert_eq!(a.a, "list_in_record3 input");
ListInRecord3Result {
a: "list_in_record3 output".to_string(),
}
}
fn list_in_record4(&mut self, a: ListInAliasParam<'_>) -> ListInAliasResult {
assert_eq!(a.a, "input4");
ListInRecord4Result {
a: "result4".to_string(),
}
}
fn list_in_variant1(
&mut self,
a: ListInVariant1V1<'_>,
b: ListInVariant1V2<'_>,
c: ListInVariant1V3<'_>,
) {
assert_eq!(a.unwrap(), "foo");
assert_eq!(b.unwrap_err(), "bar");
match c {
ListInVariant1V3::String(s) => assert_eq!(s, "baz"),
ListInVariant1V3::F32(_) => panic!(),
}
}
fn list_in_variant2(&mut self) -> Option<String> {
Some("list_in_variant2".to_string())
}
fn list_in_variant3(&mut self, a: ListInVariant3Param<'_>) -> Option<String> {
assert_eq!(a.unwrap(), "input3");
Some("output3".to_string())
}
fn errno_result(&mut self) -> Result<(), MyErrno> {
MyErrno::A.to_string();
format!("{:?}", MyErrno::A);
fn assert_error<T: std::error::Error>() {}
assert_error::<MyErrno>();
Err(MyErrno::B)
}
fn list_typedefs(
&mut self,
a: ListTypedef<'_>,
b: ListTypedef3Param<'_>,
) -> (ListTypedef2, ListTypedef3Result) {
assert_eq!(a, "typedef1");
assert_eq!(b.len(), 1);
assert_eq!(b[0], "typedef2");
(b"typedef3".to_vec(), vec!["typedef4".to_string()])
}
fn list_of_variants(
&mut self,
bools: Vec<bool>,
results: Vec<Result<(), ()>>,
enums: Vec<MyErrno>,
) -> (Vec<bool>, Vec<Result<(), ()>>, Vec<MyErrno>) {
assert_eq!(bools, [true, false]);
assert_eq!(results, [Ok(()), Err(())]);
assert_eq!(enums, [MyErrno::Success, MyErrno::A]);
(
vec![false, true],
vec![Err(()), Ok(())],
vec![MyErrno::A, MyErrno::B],
)
}
}
wit_bindgen_wasmtime::import!("../../tests/runtime/flavorful/exports.wit");
fn run(wasm: &str) -> Result<()> {
use exports::*;
let (exports, mut store) = crate::instantiate(
wasm,
|linker| imports::add_to_linker(linker, |cx| -> &mut MyImports { &mut cx.imports }),
|store, module, linker| Exports::instantiate(store, module, linker, |cx| &mut cx.exports),
)?;
exports.test_imports(&mut store)?;
exports.list_in_record1(
&mut store,
ListInRecord1 {
a: "list_in_record1",
},
)?;
assert_eq!(exports.list_in_record2(&mut store)?.a, "list_in_record2");
assert_eq!(
exports
.list_in_record3(
&mut store,
ListInRecord3Param {
a: "list_in_record3 input"
}
)?
.a,
"list_in_record3 output"
);
assert_eq!(
exports
.list_in_record4(&mut store, ListInAliasParam { a: "input4" })?
.a,
"result4"
);
exports.list_in_variant1(
&mut store,
Some("foo"),
Err("bar"),
ListInVariant1V3::String("baz"),
)?;
assert_eq!(
exports.list_in_variant2(&mut store)?,
Some("list_in_variant2".to_string())
);
assert_eq!(
exports.list_in_variant3(&mut store, Some("input3"))?,
Some("output3".to_string())
);
assert!(exports.errno_result(&mut store)?.is_err());
MyErrno::A.to_string();
format!("{:?}", MyErrno::A);
fn assert_error<T: std::error::Error>() {}
assert_error::<MyErrno>();
let (a, b) = exports.list_typedefs(&mut store, "typedef1", &["typedef2"])?;
assert_eq!(a, b"typedef3");
assert_eq!(b.len(), 1);
assert_eq!(b[0], "typedef4");
Ok(())
}

View File

@@ -0,0 +1,85 @@
import { addImportsToImports, Imports, MyErrno } from "./imports.js";
import { Exports } from "./exports.js";
import * as exports from "./exports.js";
import { getWasm, addWasiToImports } from "./helpers.js";
// @ts-ignore
import * as assert from 'assert';
async function run() {
const importObj = {};
const imports: Imports = {
listInRecord1(x) {},
listInRecord2() { return { a: 'list_in_record2' }; },
listInRecord3(x) {
assert.strictEqual(x.a, 'list_in_record3 input');
return { a: 'list_in_record3 output' };
},
listInRecord4(x) {
assert.strictEqual(x.a, 'input4');
return { a: 'result4' };
},
listInVariant1(a, b, c) {
assert.strictEqual(a, 'foo');
assert.deepStrictEqual(b, { tag: 'err', val: 'bar' });
assert.deepStrictEqual(c, { tag: 0, val: 'baz' });
},
listInVariant2() { return 'list_in_variant2'; },
listInVariant3(x) {
assert.strictEqual(x, 'input3');
return 'output3';
},
errnoResult() { return { tag: 'err', val: "b" }; },
listTypedefs(x, y) {
assert.strictEqual(x, 'typedef1');
assert.deepStrictEqual(y, ['typedef2']);
return [(new TextEncoder).encode('typedef3'), ['typedef4']];
},
listOfVariants(bools, results, enums) {
assert.deepStrictEqual(bools, [true, false]);
assert.deepStrictEqual(results, [{ tag: 'ok', val: undefined }, { tag: 'err', val: undefined }]);
assert.deepStrictEqual(enums, ["success", "a"]);
return [
[false, true],
[{ tag: 'err', val: undefined }, { tag: 'ok', val: undefined }],
["a", "b"],
];
},
};
let instance: WebAssembly.Instance;
addImportsToImports(importObj, imports, name => instance.exports[name]);
const wasi = addWasiToImports(importObj);
const wasm = new Exports();
await wasm.instantiate(getWasm(), importObj);
wasi.start(wasm.instance);
instance = wasm.instance;
wasm.testImports();
wasm.listInRecord1({ a: "list_in_record1" });
assert.deepStrictEqual(wasm.listInRecord2(), { a: "list_in_record2" });
assert.deepStrictEqual(
wasm.listInRecord3({ a: "list_in_record3 input" }),
{ a: "list_in_record3 output" },
);
assert.deepStrictEqual(
wasm.listInRecord4({ a: "input4" }),
{ a: "result4" },
);
wasm.listInVariant1("foo", { tag: 'err', val: 'bar' }, { tag: 0, val: 'baz' });
assert.deepStrictEqual(wasm.listInVariant2(), "list_in_variant2");
assert.deepStrictEqual(wasm.listInVariant3("input3"), "output3");
assert.deepStrictEqual(wasm.errnoResult().tag, 'err');
const [r1, r2] = wasm.listTypedefs("typedef1", ["typedef2"]);
assert.deepStrictEqual(r1, (new TextEncoder()).encode('typedef3'));
assert.deepStrictEqual(r2, ['typedef4']);
}
await run()

View File

@@ -0,0 +1,31 @@
record list-in-record1 { a: string }
record list-in-record2 { a: string }
record list-in-record3 { a: string }
record list-in-record4 { a: string }
type list-in-alias = list-in-record4
list-in-record1: func(a: list-in-record1)
list-in-record2: func() -> list-in-record2
list-in-record3: func(a: list-in-record3) -> list-in-record3
list-in-record4: func(a: list-in-alias) -> list-in-alias
type list-in-variant1-v1 = option<string>
type list-in-variant1-v2 = expected<unit, string>
union list-in-variant1-v3 { string, float32 }
list-in-variant1: func(a: list-in-variant1-v1, b: list-in-variant1-v2, c: list-in-variant1-v3)
type list-in-variant2 = option<string>
list-in-variant2: func() -> list-in-variant2
type list-in-variant3 = option<string>
list-in-variant3: func(a: list-in-variant3) -> list-in-variant3
enum my-errno { success, a, b }
errno-result: func() -> expected<unit, my-errno>
type list-typedef = string
type list-typedef2 = list<u8>
type list-typedef3 = list<string>
list-typedefs: func(a: list-typedef, c: list-typedef3) -> tuple<list-typedef2, list-typedef3>
list-of-variants: func(a: list<bool>, b: list<expected<unit, unit>>, c: list<my-errno>) -> tuple<list<bool>, list<expected<unit, unit>>, list<my-errno>>

View File

@@ -0,0 +1,197 @@
#include <assert.h>
#include <imports.h>
#include <exports.h>
#include <stdlib.h>
#include <string.h>
void exports_test_imports() {
{
imports_list_in_record1_t a;
imports_string_set(&a.a, "list_in_record1");
imports_list_in_record1(&a);
imports_list_in_record2_t b;
imports_list_in_record2(&b);
assert(memcmp(b.a.ptr, "list_in_record2", b.a.len) == 0);
imports_list_in_record2_free(&b);
}
{
imports_list_in_record3_t a, b;
imports_string_set(&a.a, "list_in_record3 input");
imports_list_in_record3(&a, &b);
assert(memcmp(b.a.ptr, "list_in_record3 output", b.a.len) == 0);
imports_list_in_record3_free(&b);
}
{
imports_list_in_record4_t a, b;
imports_string_set(&a.a, "input4");
imports_list_in_record4(&a, &b);
assert(memcmp(b.a.ptr, "result4", b.a.len) == 0);
imports_list_in_record4_free(&b);
}
{
imports_list_in_variant1_v1_t a;
imports_list_in_variant1_v2_t b;
imports_list_in_variant1_v3_t c;
a.is_some = true;
imports_string_set(&a.val, "foo");
b.is_err = true;
imports_string_set(&b.val.err, "bar");
c.tag = 0;
imports_string_set(&c.val.f0, "baz");
imports_list_in_variant1(&a, &b, &c);
}
{
imports_string_t a;
assert(imports_list_in_variant2(&a));
assert(memcmp(a.ptr, "list_in_variant2", a.len) == 0);
imports_string_free(&a);
}
{
imports_list_in_variant3_t a;
a.is_some = true;
imports_string_set(&a.val, "input3");
imports_string_t b;
assert(imports_list_in_variant3(&a, &b));
assert(memcmp(b.ptr, "output3", b.len) == 0);
imports_string_free(&b);
}
assert(imports_errno_result() == IMPORTS_MY_ERRNO_B);
{
imports_string_t a;
imports_string_set(&a, "typedef1");
imports_string_t b_str;
imports_string_set(&b_str, "typedef2");
imports_list_typedef3_t b;
b.ptr = &b_str;
b.len = 1;
imports_list_typedef2_t c;
imports_list_typedef3_t d;
imports_list_typedefs(&a, &b, &c, &d);
assert(memcmp(c.ptr, "typedef3", c.len) == 0);
assert(d.len == 1);
assert(memcmp(d.ptr[0].ptr, "typedef4", d.ptr[0].len) == 0);
imports_list_typedef2_free(&c);
imports_list_typedef3_free(&d);
}
{
imports_list_bool_t a;
bool a_val[] = {true, false};
a.ptr = a_val;
a.len = 2;
imports_list_expected_unit_unit_t b;
imports_expected_unit_unit_t b_val[2];
b_val[0].is_err = false;
b_val[1].is_err = true;
b.ptr = b_val;
b.len = 2;
imports_list_my_errno_t c;
imports_my_errno_t c_val[2];
c_val[0] = IMPORTS_MY_ERRNO_SUCCESS;
c_val[1] = IMPORTS_MY_ERRNO_A;
c.ptr = c_val;
c.len = 2;
imports_list_bool_t d;
imports_list_expected_unit_unit_t e;
imports_list_my_errno_t f;
imports_list_of_variants(&a, &b, &c, &d, &e, &f);
assert(d.len == 2);
assert(d.ptr[0] == false);
assert(d.ptr[1] == true);
assert(e.len == 2);
assert(e.ptr[0].is_err == true);
assert(e.ptr[1].is_err == false);
assert(f.len == 2);
assert(f.ptr[0] == IMPORTS_MY_ERRNO_A);
assert(f.ptr[1] == IMPORTS_MY_ERRNO_B);
imports_list_bool_free(&d);
imports_list_expected_unit_unit_free(&e);
imports_list_my_errno_free(&f);
}
}
void exports_list_in_record1(exports_list_in_record1_t *a) {
assert(memcmp(a->a.ptr, "list_in_record1", a->a.len) == 0);
exports_list_in_record1_free(a);
}
void exports_list_in_record2(exports_list_in_record2_t *ret0) {
exports_string_dup(&ret0->a, "list_in_record2");
}
void exports_list_in_record3(exports_list_in_record3_t *a, exports_list_in_record3_t *ret0) {
assert(memcmp(a->a.ptr, "list_in_record3 input", a->a.len) == 0);
exports_list_in_record3_free(a);
exports_string_dup(&ret0->a, "list_in_record3 output");
}
void exports_list_in_record4(exports_list_in_alias_t *a, exports_list_in_alias_t *ret0) {
assert(memcmp(a->a.ptr, "input4", a->a.len) == 0);
exports_list_in_alias_free(a);
exports_string_dup(&ret0->a, "result4");
}
void exports_list_in_variant1(exports_list_in_variant1_v1_t *a, exports_list_in_variant1_v2_t *b, exports_list_in_variant1_v3_t *c) {
assert(a->is_some);
assert(memcmp(a->val.ptr, "foo", a->val.len) == 0);
exports_list_in_variant1_v1_free(a);
assert(b->is_err);
assert(memcmp(b->val.err.ptr, "bar", b->val.err.len) == 0);
exports_list_in_variant1_v2_free(b);
assert(c->tag == 0);
assert(memcmp(c->val.f0.ptr, "baz", c->val.f0.len) == 0);
exports_list_in_variant1_v3_free(c);
}
bool exports_list_in_variant2(exports_string_t *ret0) {
exports_string_dup(ret0, "list_in_variant2");
return true;
}
bool exports_list_in_variant3(exports_list_in_variant3_t *a, exports_string_t *ret0) {
assert(a->is_some);
assert(memcmp(a->val.ptr, "input3", a->val.len) == 0);
exports_list_in_variant3_free(a);
exports_string_dup(ret0, "output3");
return true;
}
exports_my_errno_t exports_errno_result(void) {
return EXPORTS_MY_ERRNO_B;
}
void exports_list_typedefs(exports_list_typedef_t *a, exports_list_typedef3_t *c, exports_list_typedef2_t *ret0, exports_list_typedef3_t *ret1) {
assert(memcmp(a->ptr, "typedef1", a->len) == 0);
exports_list_typedef_free(a);
assert(c->len == 1);
assert(memcmp(c->ptr[0].ptr, "typedef2", c->ptr[0].len) == 0);
exports_list_typedef3_free(c);
ret0->ptr = malloc(8);
ret0->len = 8;
memcpy(ret0->ptr, "typedef3", 8);
ret1->ptr = malloc(sizeof(exports_string_t));
ret1->len = 1;
exports_string_dup(&ret1->ptr[0], "typedef4");
}

View File

@@ -0,0 +1,116 @@
wit_bindgen_rust::import!("../../tests/runtime/flavorful/imports.wit");
wit_bindgen_rust::export!("../../tests/runtime/flavorful/exports.wit");
use exports::*;
struct Exports;
impl exports::Exports for Exports {
fn test_imports() {
use imports::*;
let _guard = test_rust_wasm::guard();
list_in_record1(ListInRecord1 {
a: "list_in_record1",
});
assert_eq!(list_in_record2().a, "list_in_record2");
assert_eq!(
list_in_record3(ListInRecord3Param {
a: "list_in_record3 input"
})
.a,
"list_in_record3 output"
);
assert_eq!(
list_in_record4(ListInAliasParam { a: "input4" }).a,
"result4"
);
list_in_variant1(Some("foo"), Err("bar"), ListInVariant1V3::String("baz"));
assert_eq!(list_in_variant2(), Some("list_in_variant2".to_string()));
assert_eq!(
list_in_variant3(Some("input3")),
Some("output3".to_string())
);
assert!(errno_result().is_err());
MyErrno::A.to_string();
format!("{:?}", MyErrno::A);
fn assert_error<T: std::error::Error>() {}
assert_error::<MyErrno>();
let (a, b) = list_typedefs("typedef1", &["typedef2"]);
assert_eq!(a, b"typedef3");
assert_eq!(b.len(), 1);
assert_eq!(b[0], "typedef4");
let (a, b, c) = list_of_variants(
&[true, false],
&[Ok(()), Err(())],
&[MyErrno::Success, MyErrno::A],
);
assert_eq!(a, [false, true]);
assert_eq!(b, [Err(()), Ok(())]);
assert_eq!(c, [MyErrno::A, MyErrno::B]);
}
fn list_in_record1(ty: ListInRecord1) {
assert_eq!(ty.a, "list_in_record1");
}
fn list_in_record2() -> ListInRecord2 {
ListInRecord2 {
a: "list_in_record2".to_string(),
}
}
fn list_in_record3(a: ListInRecord3) -> ListInRecord3 {
assert_eq!(a.a, "list_in_record3 input");
ListInRecord3 {
a: "list_in_record3 output".to_string(),
}
}
fn list_in_record4(a: ListInAlias) -> ListInAlias {
assert_eq!(a.a, "input4");
ListInRecord4 {
a: "result4".to_string(),
}
}
fn list_in_variant1(a: ListInVariant1V1, b: ListInVariant1V2, c: ListInVariant1V3) {
assert_eq!(a.unwrap(), "foo");
assert_eq!(b.unwrap_err(), "bar");
match c {
ListInVariant1V3::String(s) => assert_eq!(s, "baz"),
ListInVariant1V3::F32(_) => panic!(),
}
}
fn list_in_variant2() -> Option<String> {
Some("list_in_variant2".to_string())
}
fn list_in_variant3(a: ListInVariant3) -> Option<String> {
assert_eq!(a.unwrap(), "input3");
Some("output3".to_string())
}
fn errno_result() -> Result<(), MyErrno> {
MyErrno::A.to_string();
format!("{:?}", MyErrno::A);
fn assert_error<T: std::error::Error>() {}
assert_error::<MyErrno>();
Err(MyErrno::B)
}
fn list_typedefs(a: ListTypedef, b: ListTypedef3) -> (ListTypedef2, ListTypedef3) {
assert_eq!(a, "typedef1");
assert_eq!(b.len(), 1);
assert_eq!(b[0], "typedef2");
(b"typedef3".to_vec(), vec!["typedef4".to_string()])
}
}

View File

@@ -0,0 +1,53 @@
test-imports: func()
resource wasm-state
resource wasm-state2
wasm-state-create: func() -> wasm-state
wasm-state-get-val: func(a: wasm-state) -> u32
wasm-state2-create: func() -> wasm-state2
wasm-state2-saw-close: func() -> bool
two-wasm-states: func(a: wasm-state, b: wasm-state2) -> tuple<wasm-state, wasm-state2>
record wasm-state-param-record { a: wasm-state2 }
wasm-state2-param-record: func(a: wasm-state-param-record)
type wasm-state-param-tuple = tuple<wasm-state2>
wasm-state2-param-tuple: func(a: wasm-state-param-tuple)
type wasm-state-param-option = option<wasm-state2>
wasm-state2-param-option: func(a: wasm-state-param-option)
type wasm-state-param-result = expected<wasm-state2, u32>
wasm-state2-param-result: func(a: wasm-state-param-result)
union wasm-state-param-variant { wasm-state2, u32 }
wasm-state2-param-variant: func(a: wasm-state-param-variant)
wasm-state2-param-list: func(a: list<wasm-state2>)
record wasm-state-result-record { a: wasm-state2 }
wasm-state2-result-record: func() -> wasm-state-result-record
type wasm-state-result-tuple = tuple<wasm-state2>
wasm-state2-result-tuple: func() -> wasm-state-result-tuple
type wasm-state-result-option = option<wasm-state2>
wasm-state2-result-option: func() -> wasm-state-result-option
type wasm-state-result-result = expected<wasm-state2, u32>
wasm-state2-result-result: func() -> wasm-state-result-result
union wasm-state-result-variant { wasm-state2, u32 }
wasm-state2-result-variant: func() -> wasm-state-result-variant
wasm-state2-result-list: func() -> list<wasm-state2>
resource markdown {
static create: func() -> option<markdown>
append: func(buf: string)
render: func() -> string
}

View File

@@ -0,0 +1,180 @@
from dataclasses import dataclass
from exports.bindings import Exports
from imports.bindings import add_imports_to_linker, Imports
from typing import Tuple, List
import exports.bindings as e
import imports.bindings as i
import sys
import wasmtime
@dataclass
class HostState(i.HostState):
val: int
def __init__(self, val: int) -> None:
self.val = val
HOST_STATE2_CLOSED = False
@dataclass
class HostState2(i.HostState2):
val: int
def __init__(self, val: int) -> None:
self.val = val
def drop(self) -> None:
global HOST_STATE2_CLOSED
HOST_STATE2_CLOSED = True
@dataclass
class Markdown(i.Markdown2):
buf: str = ''
def append(self, data: str) -> None:
self.buf += data
def render(self) -> str:
return self.buf.replace('red', 'green')
class OddName(i.OddName):
def frob_the_odd(self) -> None:
pass
class MyImports:
def host_state_create(self) -> i.HostState:
return HostState(100)
def host_state_get(self, a: i.HostState) -> int:
assert(isinstance(a, HostState))
return a.val
def host_state2_create(self) -> i.HostState2:
return HostState2(101)
def host_state2_saw_close(self) -> bool:
return HOST_STATE2_CLOSED
def two_host_states(self, a: i.HostState, b: i.HostState2) -> Tuple[i.HostState, i.HostState2]:
return (b, a)
def host_state2_param_record(self, a: i.HostStateParamRecord) -> None:
pass
def host_state2_param_tuple(self, a: i.HostStateParamTuple) -> None:
pass
def host_state2_param_option(self, a: i.HostStateParamOption) -> None:
pass
def host_state2_param_result(self, a: i.HostStateParamResult) -> None:
pass
def host_state2_param_variant(self, a: i.HostStateParamVariant) -> None:
pass
def host_state2_param_list(self, a: List[i.HostState2]) -> None:
pass
def host_state2_result_record(self) -> i.HostStateResultRecord:
return i.HostStateResultRecord(HostState(2))
def host_state2_result_tuple(self) -> i.HostStateResultTuple:
return (HostState(2),)
def host_state2_result_option(self) -> i.HostStateResultOption:
return HostState(2)
def host_state2_result_result(self) -> i.HostStateResultResult:
return i.Ok(HostState2(2))
def host_state2_result_variant(self) -> i.HostStateResultVariant:
return HostState2(2)
def host_state2_result_list(self) -> List[i.HostState2]:
return [HostState2(2), HostState2(5)]
def markdown2_create(self) -> i.Markdown2:
return Markdown()
def odd_name_create(self) -> i.OddName:
return OddName()
def run(wasm_file: str) -> None:
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, wasm_file)
linker = wasmtime.Linker(store.engine)
linker.define_wasi()
wasi = wasmtime.WasiConfig()
wasi.inherit_stdout()
wasi.inherit_stderr()
store.set_wasi(wasi)
imports = MyImports()
add_imports_to_linker(linker, store, imports)
wasm = Exports(store, linker, module)
wasm.test_imports(store)
# Param/result of a handle works in a simple fashion
s: e.WasmState = wasm.wasm_state_create(store)
assert(wasm.wasm_state_get_val(store, s) == 100)
# Deterministic destruction is possible
assert(wasm.wasm_state2_saw_close(store) == False)
s2: e.WasmState2 = wasm.wasm_state2_create(store)
assert(wasm.wasm_state2_saw_close(store) == False)
s2.drop(store)
assert(wasm.wasm_state2_saw_close(store) == True)
arg1 = wasm.wasm_state_create(store)
arg2 = wasm.wasm_state2_create(store)
c, d = wasm.two_wasm_states(store, arg1, arg2)
arg1.drop(store)
arg2.drop(store)
wasm.wasm_state2_param_record(store, e.WasmStateParamRecord(d))
wasm.wasm_state2_param_tuple(store, (d,))
wasm.wasm_state2_param_option(store, d)
wasm.wasm_state2_param_option(store, None)
wasm.wasm_state2_param_result(store, e.Ok(d))
wasm.wasm_state2_param_result(store, e.Err(2))
wasm.wasm_state2_param_variant(store, d)
wasm.wasm_state2_param_variant(store, 2)
wasm.wasm_state2_param_list(store, [])
wasm.wasm_state2_param_list(store, [d])
wasm.wasm_state2_param_list(store, [d, d])
c.drop(store)
d.drop(store)
wasm.wasm_state2_result_record(store).a.drop(store)
wasm.wasm_state2_result_tuple(store)[0].drop(store)
opt = wasm.wasm_state2_result_option(store)
assert(opt is not None)
opt.drop(store)
result = wasm.wasm_state2_result_result(store)
assert(isinstance(result, e.Ok))
result.value.drop(store)
variant = wasm.wasm_state2_result_variant(store)
print(variant)
assert(isinstance(variant, e.WasmState2))
variant.drop(store)
for val in wasm.wasm_state2_result_list(store):
val.drop(store)
s.drop(store)
md = e.Markdown.create(store, wasm)
if md:
md.append(store, "red is the best color")
assert(md.render(store) == "green is the best color")
md.drop(store)
if __name__ == '__main__':
run(sys.argv[1])

View File

@@ -0,0 +1,156 @@
wit_bindgen_wasmtime::export!("../../tests/runtime/handles/imports.wit");
use anyhow::Result;
use imports::*;
use std::cell::RefCell;
#[derive(Default)]
pub struct MyImports {
host_state2_closed: bool,
}
#[derive(Debug)]
pub struct SuchState(u32);
#[derive(Default, Debug)]
pub struct Markdown {
buf: RefCell<String>,
}
impl Imports for MyImports {
type HostState = SuchState;
type HostState2 = ();
type Markdown2 = Markdown;
type OddName = ();
fn host_state_create(&mut self) -> SuchState {
SuchState(100)
}
fn host_state_get(&mut self, state: &SuchState) -> u32 {
state.0
}
fn host_state2_create(&mut self) {}
fn host_state2_saw_close(&mut self) -> bool {
self.host_state2_closed
}
fn drop_host_state2(&mut self, _state: ()) {
self.host_state2_closed = true;
}
fn two_host_states(&mut self, _a: &SuchState, _b: &()) -> (SuchState, ()) {
(SuchState(2), ())
}
fn host_state2_param_record(&mut self, _a: HostStateParamRecord<'_, Self>) {}
fn host_state2_param_tuple(&mut self, _a: (&'_ (),)) {}
fn host_state2_param_option(&mut self, _a: Option<&'_ ()>) {}
fn host_state2_param_result(&mut self, _a: Result<&'_ (), u32>) {}
fn host_state2_param_variant(&mut self, _a: HostStateParamVariant<'_, Self>) {}
fn host_state2_param_list(&mut self, _a: Vec<&()>) {}
fn host_state2_result_record(&mut self) -> HostStateResultRecord<Self> {
HostStateResultRecord { a: () }
}
fn host_state2_result_tuple(&mut self) -> ((),) {
((),)
}
fn host_state2_result_option(&mut self) -> Option<()> {
Some(())
}
fn host_state2_result_result(&mut self) -> Result<(), u32> {
Ok(())
}
fn host_state2_result_variant(&mut self) -> HostStateResultVariant<Self> {
HostStateResultVariant::HostState2(())
}
fn host_state2_result_list(&mut self) -> Vec<()> {
vec![(), ()]
}
fn markdown2_create(&mut self) -> Markdown {
Markdown::default()
}
fn markdown2_append(&mut self, md: &Markdown, buf: &str) {
md.buf.borrow_mut().push_str(buf);
}
fn markdown2_render(&mut self, md: &Markdown) -> String {
md.buf.borrow().replace("red", "green")
}
fn odd_name_create(&mut self) {}
fn odd_name_frob_the_odd(&mut self, _: &()) {}
}
wit_bindgen_wasmtime::import!("../../tests/runtime/handles/exports.wit");
fn run(wasm: &str) -> Result<()> {
use exports::*;
let (exports, mut store) = crate::instantiate(
wasm,
|linker| {
imports::add_to_linker(
linker,
|cx: &mut crate::Context<(MyImports, imports::ImportsTables<MyImports>), _>| {
(&mut cx.imports.0, &mut cx.imports.1)
},
)
},
|store, module, linker| Exports::instantiate(store, module, linker, |cx| &mut cx.exports),
)?;
exports.test_imports(&mut store)?;
let s: WasmState = exports.wasm_state_create(&mut store)?;
assert_eq!(exports.wasm_state_get_val(&mut store, &s)?, 100);
exports.drop_wasm_state(&mut store, s)?;
assert_eq!(exports.wasm_state2_saw_close(&mut store)?, false);
let s: WasmState2 = exports.wasm_state2_create(&mut store)?;
assert_eq!(exports.wasm_state2_saw_close(&mut store)?, false);
exports.drop_wasm_state2(&mut store, s)?;
assert_eq!(exports.wasm_state2_saw_close(&mut store)?, true);
let a = exports.wasm_state_create(&mut store)?;
let b = exports.wasm_state2_create(&mut store)?;
let (s1, s2) = exports.two_wasm_states(&mut store, &a, &b)?;
exports.drop_wasm_state(&mut store, a)?;
exports.drop_wasm_state(&mut store, s1)?;
exports.drop_wasm_state2(&mut store, b)?;
exports.wasm_state2_param_record(&mut store, WasmStateParamRecord { a: &s2 })?;
exports.wasm_state2_param_tuple(&mut store, (&s2,))?;
exports.wasm_state2_param_option(&mut store, Some(&s2))?;
exports.wasm_state2_param_option(&mut store, None)?;
exports.wasm_state2_param_result(&mut store, Ok(&s2))?;
exports.wasm_state2_param_result(&mut store, Err(2))?;
exports.wasm_state2_param_variant(&mut store, WasmStateParamVariant::WasmState2(&s2))?;
exports.wasm_state2_param_variant(&mut store, WasmStateParamVariant::U32(2))?;
exports.wasm_state2_param_list(&mut store, &[])?;
exports.wasm_state2_param_list(&mut store, &[&s2])?;
exports.wasm_state2_param_list(&mut store, &[&s2, &s2])?;
exports.drop_wasm_state2(&mut store, s2)?;
let s = exports.wasm_state2_result_record(&mut store)?.a;
exports.drop_wasm_state2(&mut store, s)?;
let s = exports.wasm_state2_result_tuple(&mut store)?.0;
exports.drop_wasm_state2(&mut store, s)?;
let s = exports.wasm_state2_result_option(&mut store)?.unwrap();
exports.drop_wasm_state2(&mut store, s)?;
let s = exports.wasm_state2_result_result(&mut store)?.unwrap();
match exports.wasm_state2_result_variant(&mut store)? {
WasmStateResultVariant::WasmState2(s) => exports.drop_wasm_state2(&mut store, s)?,
WasmStateResultVariant::U32(_) => panic!(),
}
exports.drop_wasm_state2(&mut store, s)?;
for s in exports.wasm_state2_result_list(&mut store)? {
exports.drop_wasm_state2(&mut store, s)?;
}
Ok(())
}

View File

@@ -0,0 +1,127 @@
import { addImportsToImports, Imports } from "./imports.js";
import { Exports } from "./exports.js";
import * as exports from "./exports.js";
import { getWasm, addWasiToImports } from "./helpers.js";
// @ts-ignore
import * as assert from 'assert';
async function run() {
const importObj = {};
let sawClose = false;
const imports: Imports = {
hostStateCreate() { return 100; },
hostStateGet(x) { return x as number; },
hostState2Create() { return 101; },
hostState2SawClose() { return sawClose; },
dropHostState2(state) { sawClose = true; },
twoHostStates(a, b) { return [b, a]; },
hostState2ParamRecord(x) {},
hostState2ParamTuple(x) {},
hostState2ParamOption(x) {},
hostState2ParamResult(x) {},
hostState2ParamVariant(x) {},
hostState2ParamList(x) {},
hostState2ResultRecord() { return { a: {} }; },
hostState2ResultTuple() { return [{}]; },
hostState2ResultOption() { return 102; },
hostState2ResultResult() { return { tag: 'ok', val: {} }; },
hostState2ResultVariant() { return { tag: 0, val: {} }; },
hostState2ResultList() { return [{}, 3]; },
markdown2Create() {
class Markdown {
buf: string;
constructor() {
this.buf = '';
}
append(extra: string) {
this.buf += extra;
}
render() {
return this.buf.replace('red', 'green');
}
}
return new Markdown();
},
oddNameCreate() {
class OddName {
frobTheOdd() {}
}
return new OddName();
}
};
let instance: WebAssembly.Instance;
addImportsToImports(importObj, imports, name => instance.exports[name]);
const wasi = addWasiToImports(importObj);
const wasm = new Exports();
await wasm.instantiate(getWasm(), importObj);
wasi.start(wasm.instance);
instance = wasm.instance;
wasm.testImports();
// Param/result of a handle works in a simple fashion
const s: exports.WasmState = wasm.wasmStateCreate();
assert.strictEqual(wasm.wasmStateGetVal(s), 100);
// Deterministic destruction is possible
assert.strictEqual(wasm.wasmState2SawClose(), false);
const s2: exports.WasmState2 = wasm.wasmState2Create();
assert.strictEqual(wasm.wasmState2SawClose(), false);
s2.drop();
assert.strictEqual(wasm.wasmState2SawClose(), true);
const arg1 = wasm.wasmStateCreate();
const arg2 = wasm.wasmState2Create();
const [c, d] = wasm.twoWasmStates(arg1, arg2);
arg1.drop();
arg2.drop();
wasm.wasmState2ParamRecord({ a: d });
wasm.wasmState2ParamTuple([d]);
wasm.wasmState2ParamOption(d);
wasm.wasmState2ParamOption(null);
wasm.wasmState2ParamResult({ tag: 'ok', val: d });
wasm.wasmState2ParamResult({ tag: 'err', val: 2 });
wasm.wasmState2ParamVariant({ tag: 0, val: d });
wasm.wasmState2ParamVariant({ tag: 1, val: 2 });
wasm.wasmState2ParamList([]);
wasm.wasmState2ParamList([d]);
wasm.wasmState2ParamList([d, d]);
c.drop();
d.drop();
wasm.wasmState2ResultRecord().a?.drop();
wasm.wasmState2ResultTuple()[0].drop();
const opt = wasm.wasmState2ResultOption();
if (opt === null)
throw new Error('should be some');
opt.drop();
const result = wasm.wasmState2ResultResult();
if (result.tag === 'err')
throw new Error('should be ok');
result.val.drop();
const variant = wasm.wasmState2ResultVariant();
if (variant.tag === 1)
throw new Error('should be 0');
variant.val.drop();
for (let val of wasm.wasmState2ResultList())
val.drop();
s.drop();
const md = exports.Markdown.create(wasm);
if (md) {
md.append("red is the best color");
assert.strictEqual(md.render(), "green is the best color");
md.drop();
}
}
await run()

View File

@@ -0,0 +1,55 @@
resource host-state
resource host-state2
host-state-create: func() -> host-state
host-state-get: func(a: host-state) -> u32
host-state2-create: func() -> host-state2
host-state2-saw-close: func() -> bool
two-host-states: func(a: host-state, b: host-state2) -> tuple<host-state, host-state2>
record host-state-param-record { a: host-state2 }
host-state2-param-record: func(a: host-state-param-record)
type host-state-param-tuple = tuple<host-state2>
host-state2-param-tuple: func(a: host-state-param-tuple)
type host-state-param-option = option<host-state2>
host-state2-param-option: func(a: host-state-param-option)
type host-state-param-result = expected<host-state2, u32>
host-state2-param-result: func(a: host-state-param-result)
union host-state-param-variant { host-state2, u32 }
host-state2-param-variant: func(a: host-state-param-variant)
host-state2-param-list: func(a: list<host-state2>)
record host-state-result-record { a: host-state2 }
host-state2-result-record: func() -> host-state-result-record
type host-state-result-tuple = tuple<host-state2>
host-state2-result-tuple: func() -> host-state-result-tuple
type host-state-result-option = option<host-state2>
host-state2-result-option: func() -> host-state-result-option
type host-state-result-result = expected<host-state2, u32>
host-state2-result-result: func() -> host-state-result-result
union host-state-result-variant { host-state2, u32 }
host-state2-result-variant: func() -> host-state-result-variant
host-state2-result-list: func() -> list<host-state2>
resource markdown2 {
static create: func() -> markdown2
append: func(buf: string)
render: func() -> string
}
resource %odd-name {
static create: func() -> %odd-name
%frob-the-odd: func()
}

View File

@@ -0,0 +1,223 @@
#include <assert.h>
#include <exports.h>
#include <imports.h>
#include <stdlib.h>
#include <string.h>
void exports_test_imports() {
imports_host_state_t s = imports_host_state_create();
assert(imports_host_state_get(s) == 100);
imports_host_state_free(&s);
assert(imports_host_state2_saw_close() == false);
imports_host_state2_t s2 = imports_host_state2_create();
assert(imports_host_state2_saw_close() == false);
imports_host_state2_free(&s2);
assert(imports_host_state2_saw_close() == true);
{
imports_host_state_t a, b;
imports_host_state2_t c, d;
a = imports_host_state_create();
c = imports_host_state2_create();
imports_two_host_states(a, c, &b, &d);
imports_host_state_free(&a);
imports_host_state_free(&b);
imports_host_state2_free(&c);
{
imports_host_state_param_record_t a;
a.a = d;
imports_host_state2_param_record(&a);
}
{
imports_host_state_param_tuple_t a;
a.f0 = d;
imports_host_state2_param_tuple(&a);
}
{
imports_host_state_param_option_t a;
a.is_some = true;
a.val = d;
imports_host_state2_param_option(&a);
}
{
imports_host_state_param_result_t a;
a.is_err = false;
a.val.ok = d;
imports_host_state2_param_result(&a);
a.is_err = true;
a.val.err = 2;
imports_host_state2_param_result(&a);
}
{
imports_host_state_param_variant_t a;
a.tag = 0;
a.val.f0 = d;
imports_host_state2_param_variant(&a);
a.tag = 1;
a.val.f1 = 2;
imports_host_state2_param_variant(&a);
}
{
imports_host_state2_t arr[2];
arr[0] = d;
arr[1] = d;
imports_list_host_state2_t list;
list.len = 0;
list.ptr = arr;
imports_host_state2_param_list(&list);
list.len = 1;
imports_host_state2_param_list(&list);
list.len = 2;
imports_host_state2_param_list(&list);
}
imports_host_state2_free(&d);
}
{
imports_host_state_result_record_t a;
imports_host_state2_result_record(&a);
imports_host_state2_free(&a.a);
}
{
imports_host_state2_t a;
imports_host_state2_result_tuple(&a);
imports_host_state2_free(&a);
}
{
imports_host_state2_t a;
assert(imports_host_state2_result_option(&a));
imports_host_state2_free(&a);
}
{
imports_host_state_result_result_t a;
imports_host_state2_result_result(&a);
assert(!a.is_err);
imports_host_state2_free(&a.val.ok);
}
{
imports_host_state_result_variant_t a;
imports_host_state2_result_variant(&a);
assert(a.tag == 0);
imports_host_state2_free(&a.val.f0);
}
{
imports_list_host_state2_t a;
imports_host_state2_result_list(&a);
imports_list_host_state2_free(&a);
}
{
imports_markdown2_t a = imports_markdown2_create();
imports_string_t s;
imports_string_set(&s, "red is the best color");
imports_markdown2_append(a, &s);
imports_markdown2_render(a, &s);
const char *expected = "green is the best color";
assert(s.len == strlen(expected));
assert(memcmp(s.ptr, expected, s.len) == 0);
imports_string_free(&s);
imports_markdown2_free(&a);
}
}
exports_wasm_state_t exports_wasm_state_create(void) {
return exports_wasm_state_new((void*) 100);
}
uint32_t exports_wasm_state_get_val(exports_wasm_state_t a) {
uint32_t ret = (uint32_t) exports_wasm_state_get(&a);
exports_wasm_state_free(&a);
return ret;
}
exports_wasm_state2_t exports_wasm_state2_create(void) {
return exports_wasm_state2_new((void*) 33);
}
static bool WASM_STATE2_CLOSED = false;
bool exports_wasm_state2_saw_close(void) {
return WASM_STATE2_CLOSED;
}
void exports_wasm_state2_dtor(void *data) {
WASM_STATE2_CLOSED = true;
}
void exports_two_wasm_states(exports_wasm_state_t a, exports_wasm_state2_t b, exports_wasm_state_t *ret0, exports_wasm_state2_t *ret1) {
exports_wasm_state_free(&a);
exports_wasm_state2_free(&b);
*ret0 = exports_wasm_state_new((void*) 101);
*ret1 = exports_wasm_state2_new((void*) 102);
}
void exports_wasm_state2_param_record(exports_wasm_state_param_record_t *a) {
exports_wasm_state_param_record_free(a);
}
void exports_wasm_state2_param_tuple(exports_wasm_state_param_tuple_t *a) {
exports_wasm_state_param_tuple_free(a);
}
void exports_wasm_state2_param_option(exports_wasm_state_param_option_t *a) {
exports_wasm_state_param_option_free(a);
}
void exports_wasm_state2_param_result(exports_wasm_state_param_result_t *a) {
exports_wasm_state_param_result_free(a);
}
void exports_wasm_state2_param_variant(exports_wasm_state_param_variant_t *a) {
exports_wasm_state_param_variant_free(a);
}
void exports_wasm_state2_param_list(exports_list_wasm_state2_t *a) {
exports_list_wasm_state2_free(a);
}
void exports_wasm_state2_result_record(exports_wasm_state_result_record_t *ret0) {
ret0->a = exports_wasm_state2_new((void*) 222);
}
void exports_wasm_state2_result_tuple(exports_wasm_state2_t *ret0) {
*ret0 = exports_wasm_state2_new((void*) 333);
}
bool exports_wasm_state2_result_option(exports_wasm_state2_t *ret0) {
*ret0 = exports_wasm_state2_new((void*) 444);
return true;
}
void exports_wasm_state2_result_result(exports_wasm_state_result_result_t *ret0) {
ret0->is_err = false;
ret0->val.ok = exports_wasm_state2_new((void*) 555);
}
void exports_wasm_state2_result_variant(exports_wasm_state_result_variant_t *ret0) {
ret0->tag = 0;
ret0->val.f0 = exports_wasm_state2_new((void*) 666);
}
void exports_wasm_state2_result_list(exports_list_wasm_state2_t *ret0) {
ret0->len = 2;
ret0->ptr = malloc(2 * sizeof(exports_wasm_state2_t));
ret0->ptr[0] = exports_wasm_state2_new((void*) 777);
ret0->ptr[1] = exports_wasm_state2_new((void*) 888);
}
bool exports_markdown_create(exports_markdown_t *md) {
return false;
}
void exports_markdown_append(exports_markdown_t md, exports_string_t *s) {
abort();
}
void exports_markdown_render(exports_markdown_t md, exports_string_t *ret) {
abort();
}

View File

@@ -0,0 +1,131 @@
wit_bindgen_rust::import!("../../tests/runtime/handles/imports.wit");
wit_bindgen_rust::export!("../../tests/runtime/handles/exports.wit");
use exports::*;
use std::cell::RefCell;
use std::sync::atomic::{AtomicU32, Ordering::SeqCst};
use wit_bindgen_rust::Handle;
struct Exports;
static CLOSED: AtomicU32 = AtomicU32::new(0);
pub struct WasmState(u32);
pub struct WasmState2(u32);
impl exports::Exports for Exports {
fn test_imports() {
use imports::*;
let s: HostState = host_state_create();
assert_eq!(host_state_get(&s), 100);
assert_eq!(host_state2_saw_close(), false);
let s: HostState2 = host_state2_create();
assert_eq!(host_state2_saw_close(), false);
drop(s);
assert_eq!(host_state2_saw_close(), true);
let (_a, s2) = two_host_states(&host_state_create(), &host_state2_create());
host_state2_param_record(HostStateParamRecord { a: &s2 });
host_state2_param_tuple((&s2,));
host_state2_param_option(Some(&s2));
host_state2_param_option(None);
host_state2_param_result(Ok(&s2));
host_state2_param_result(Err(2));
host_state2_param_variant(HostStateParamVariant::HostState2(&s2));
host_state2_param_variant(HostStateParamVariant::U32(2));
host_state2_param_list(&[]);
host_state2_param_list(&[&s2]);
host_state2_param_list(&[&s2, &s2]);
drop(host_state2_result_record().a);
drop(host_state2_result_tuple().0);
drop(host_state2_result_option().unwrap());
drop(host_state2_result_result().unwrap());
drop(host_state2_result_variant());
drop(host_state2_result_list());
let md = Markdown2::create();
md.append("red is the best color");
assert_eq!(md.render(), "green is the best color");
let odd = OddName::create();
odd.frob_the_odd();
}
fn wasm_state_create() -> Handle<WasmState> {
WasmState(100).into()
}
fn wasm_state_get_val(state: Handle<WasmState>) -> u32 {
state.0
}
fn wasm_state2_create() -> Handle<WasmState2> {
WasmState2(33).into()
}
fn wasm_state2_saw_close() -> bool {
CLOSED.load(SeqCst) != 0
}
fn drop_wasm_state2(_state: WasmState2) {
CLOSED.store(1, SeqCst);
}
fn two_wasm_states(
_a: Handle<WasmState>,
_b: Handle<WasmState2>,
) -> (Handle<WasmState>, Handle<WasmState2>) {
(WasmState(101).into(), WasmState2(102).into())
}
fn wasm_state2_param_record(_a: WasmStateParamRecord) {}
fn wasm_state2_param_tuple(_a: (Handle<WasmState2>,)) {}
fn wasm_state2_param_option(_a: Option<Handle<WasmState2>>) {}
fn wasm_state2_param_result(_a: Result<Handle<WasmState2>, u32>) {}
fn wasm_state2_param_variant(_a: WasmStateParamVariant) {}
fn wasm_state2_param_list(_a: Vec<Handle<WasmState2>>) {}
fn wasm_state2_result_record() -> WasmStateResultRecord {
WasmStateResultRecord {
a: WasmState2(222).into(),
}
}
fn wasm_state2_result_tuple() -> (Handle<WasmState2>,) {
(WasmState2(333).into(),)
}
fn wasm_state2_result_option() -> Option<Handle<WasmState2>> {
Some(WasmState2(444).into())
}
fn wasm_state2_result_result() -> Result<Handle<WasmState2>, u32> {
Ok(WasmState2(555).into())
}
fn wasm_state2_result_variant() -> WasmStateResultVariant {
WasmStateResultVariant::WasmState2(Handle::new(WasmState2(666)))
}
fn wasm_state2_result_list() -> Vec<Handle<WasmState2>> {
vec![WasmState2(777).into(), WasmState2(888).into()]
}
}
#[derive(Default)]
pub struct Markdown {
buf: RefCell<String>,
}
impl exports::Markdown for Markdown {
fn create() -> Option<Handle<Markdown>> {
Some(Markdown::default().into())
}
fn append(&self, input: String) {
self.buf.borrow_mut().push_str(&input);
}
fn render(&self) -> String {
self.buf.borrow().replace("red", "green")
}
}

View File

@@ -0,0 +1,9 @@
invalid-u8: func()
invalid-s8: func()
invalid-u16: func()
invalid-s16: func()
invalid-char: func()
invalid-bool: func()
invalid-enum: func()
invalid-handle: func()
invalid-handle-close: func()

View File

@@ -0,0 +1,74 @@
from exports.bindings import Exports
from imports.bindings import add_imports_to_linker, Imports
from typing import Callable
import imports.bindings as i
import sys
import wasmtime
class MyImports(Imports):
def roundtrip_u8(self, x: int) -> int:
raise Exception('unreachable')
def roundtrip_s8(self, x: int) -> int:
raise Exception('unreachable')
def roundtrip_u16(self, x: int) -> int:
raise Exception('unreachable')
def roundtrip_s16(self, x: int) -> int:
raise Exception('unreachable')
def roundtrip_bool(self, x: bool) -> bool:
raise Exception('unreachable')
def roundtrip_char(self, x: str) -> str:
raise Exception('unreachable')
def roundtrip_enum(self, x: i.E) -> i.E:
raise Exception('unreachable')
def get_internal(self, x: i.HostState) -> int:
raise Exception('unreachable')
def run(wasm_file: str) -> None:
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, wasm_file)
linker = wasmtime.Linker(store.engine)
linker.define_wasi()
wasi = wasmtime.WasiConfig()
wasi.inherit_stdout()
wasi.inherit_stderr()
store.set_wasi(wasi)
imports = MyImports()
add_imports_to_linker(linker, store, imports)
wasm = Exports(store, linker, module)
def assert_throws(f: Callable, msg: str) -> None:
try:
f()
raise RuntimeError('expected exception')
except TypeError as e:
actual = str(e)
except OverflowError as e:
actual = str(e)
except ValueError as e:
actual = str(e)
except IndexError as e:
actual = str(e)
if not msg in actual:
print(actual)
assert(msg in actual)
assert_throws(lambda: wasm.invalid_bool(store), 'invalid variant discriminant for bool')
assert_throws(lambda: wasm.invalid_u8(store), 'must be between')
assert_throws(lambda: wasm.invalid_s8(store), 'must be between')
assert_throws(lambda: wasm.invalid_u16(store), 'must be between')
assert_throws(lambda: wasm.invalid_s16(store), 'must be between')
assert_throws(lambda: wasm.invalid_char(store), 'not a valid char')
assert_throws(lambda: wasm.invalid_enum(store), 'not a valid E')
assert_throws(lambda: wasm.invalid_handle(store), 'handle index not valid')
assert_throws(lambda: wasm.invalid_handle_close(store), 'handle index not valid')
if __name__ == '__main__':
run(sys.argv[1])

View File

@@ -0,0 +1,99 @@
wit_bindgen_wasmtime::export!("../../tests/runtime/invalid/imports.wit");
use anyhow::Result;
use imports::*;
use wasmtime::Trap;
#[derive(Default)]
pub struct MyImports;
impl Imports for MyImports {
type HostState = ();
fn roundtrip_u8(&mut self, _: u8) -> u8 {
unreachable!()
}
fn roundtrip_s8(&mut self, _: i8) -> i8 {
unreachable!()
}
fn roundtrip_u16(&mut self, _: u16) -> u16 {
unreachable!()
}
fn roundtrip_s16(&mut self, _: i16) -> i16 {
unreachable!()
}
fn roundtrip_char(&mut self, _: char) -> char {
unreachable!()
}
fn roundtrip_bool(&mut self, _: bool) -> bool {
unreachable!()
}
fn roundtrip_enum(&mut self, _: imports::E) -> imports::E {
unreachable!()
}
fn get_internal(&mut self, _: &()) -> u32 {
unreachable!()
}
}
wit_bindgen_wasmtime::import!("../../tests/runtime/invalid/exports.wit");
fn run(wasm: &str) -> Result<()> {
use exports::*;
let (exports, mut store) = crate::instantiate(
wasm,
|linker| {
imports::add_to_linker(
linker,
|cx: &mut crate::Context<(MyImports, imports::ImportsTables<MyImports>), _>| {
(&mut cx.imports.0, &mut cx.imports.1)
},
)
},
|store, module, linker| Exports::instantiate(store, module, linker, |cx| &mut cx.exports),
)?;
assert_err(
exports.invalid_bool(&mut store),
"invalid discriminant for `bool`",
)?;
assert_err(
exports.invalid_u8(&mut store),
"out-of-bounds integer conversion",
)?;
assert_err(
exports.invalid_s8(&mut store),
"out-of-bounds integer conversion",
)?;
assert_err(
exports.invalid_u16(&mut store),
"out-of-bounds integer conversion",
)?;
assert_err(
exports.invalid_s16(&mut store),
"out-of-bounds integer conversion",
)?;
assert_err(
exports.invalid_char(&mut store),
"char value out of valid range",
)?;
assert_err(
exports.invalid_enum(&mut store),
"invalid discriminant for `E`",
)?;
assert_err(exports.invalid_handle(&mut store), "invalid handle index")?;
assert_err(
exports.invalid_handle_close(&mut store),
"invalid handle index",
)?;
return Ok(());
fn assert_err(result: Result<(), Trap>, err: &str) -> Result<()> {
match result {
Ok(()) => anyhow::bail!("export didn't trap"),
Err(e) if e.to_string().contains(err) => Ok(()),
Err(e) => Err(e.into()),
}
}
}

View File

@@ -0,0 +1,39 @@
import { addImportsToImports, Imports } from "./imports.js";
import { Exports } from "./exports.js";
import { getWasm, addWasiToImports } from "./helpers.js";
// @ts-ignore
import * as assert from 'assert';
async function run() {
const importObj = {};
const imports: Imports = {
roundtripU8(x) { throw new Error('unreachable'); },
roundtripS8(x) { throw new Error('unreachable'); },
roundtripU16(x) { throw new Error('unreachable'); },
roundtripS16(x) { throw new Error('unreachable'); },
roundtripBool(x) { throw new Error('unreachable'); },
roundtripChar(x) { throw new Error('unreachable'); },
roundtripEnum(x) { throw new Error('unreachable'); },
getInternal(x) { throw new Error('unreachable'); },
};
let instance: WebAssembly.Instance;
addImportsToImports(importObj, imports);
const wasi = addWasiToImports(importObj);
const wasm = new Exports();
await wasm.instantiate(getWasm(), importObj);
wasi.start(wasm.instance);
instance = wasm.instance;
assert.throws(() => wasm.invalidBool(), /invalid variant discriminant for bool/);
assert.throws(() => wasm.invalidU8(), /must be between/);
assert.throws(() => wasm.invalidS8(), /must be between/);
assert.throws(() => wasm.invalidU16(), /must be between/);
assert.throws(() => wasm.invalidS16(), /must be between/);
assert.throws(() => wasm.invalidChar(), /not a valid char/);
assert.throws(() => wasm.invalidEnum(), /invalid discriminant specified for E/);
assert.throws(() => wasm.invalidHandle(), /handle index not valid/);
assert.throws(() => wasm.invalidHandleClose(), /handle index not valid/);
}
await run()

View File

@@ -0,0 +1,13 @@
roundtrip-u8: func(a: u8) -> u8
roundtrip-s8: func(a: s8) -> s8
roundtrip-u16: func(a: u16) -> u16
roundtrip-s16: func(a: s16) -> s16
roundtrip-char: func(a: char) -> char
enum e { a, b, c }
roundtrip-enum: func(a: e) -> e
roundtrip-bool: func(a: bool) -> bool
resource host-state
get-internal: func(a: host-state) -> u32

View File

@@ -0,0 +1,88 @@
wit_bindgen_rust::export!("../../tests/runtime/invalid/exports.wit");
#[link(wasm_import_module = "imports")]
extern "C" {
#[link_name = "roundtrip-bool"]
fn roundtrip_bool(a: i32) -> i32;
#[link_name = "roundtrip-u16"]
fn roundtrip_u16(a: i32) -> i32;
#[link_name = "roundtrip-u8"]
fn roundtrip_u8(a: i32) -> i32;
#[link_name = "roundtrip-s16"]
fn roundtrip_s16(a: i32) -> i32;
#[link_name = "roundtrip-s8"]
fn roundtrip_s8(a: i32) -> i32;
#[link_name = "roundtrip-char"]
fn roundtrip_char(a: i32) -> i32;
#[link_name = "roundtrip-enum"]
fn roundtrip_enum(a: i32) -> i32;
#[link_name = "get-internal"]
fn get_internal(a: i32) -> i32;
}
#[link(wasm_import_module = "canonical_abi")]
extern "C" {
#[link_name = "resource_drop_host-state"]
fn resource_drop_host_state(a: i32);
}
struct Exports;
impl exports::Exports for Exports {
fn invalid_u8() {
unsafe {
roundtrip_u8(i32::MAX);
}
unreachable!();
}
fn invalid_s8() {
unsafe {
roundtrip_s8(i32::MAX);
}
unreachable!();
}
fn invalid_u16() {
unsafe {
roundtrip_u16(i32::MAX);
}
unreachable!();
}
fn invalid_s16() {
unsafe {
roundtrip_s16(i32::MAX);
}
unreachable!();
}
fn invalid_char() {
unsafe {
roundtrip_char(0xd800);
}
unreachable!();
}
fn invalid_bool() {
unsafe {
roundtrip_bool(2);
}
unreachable!();
}
fn invalid_enum() {
unsafe {
roundtrip_enum(400);
}
unreachable!();
}
fn invalid_handle() {
unsafe {
get_internal(100);
}
unreachable!();
}
fn invalid_handle_close() {
unsafe {
resource_drop_host_state(100);
}
unreachable!();
}
}

View File

@@ -0,0 +1 @@
nop: func()

View File

@@ -0,0 +1,20 @@
import { Exports } from "./exports.js";
import { getWasm } from "./helpers.js";
async function run() {
const importObj = {};
const wasm = new Exports();
await wasm.instantiate(getWasm(), importObj);
// test other methods of creating a wasm wrapper
(new Exports()).instantiate(getWasm().buffer, importObj);
(new Exports()).instantiate(new Uint8Array(getWasm()), importObj);
(new Exports()).instantiate(new WebAssembly.Module(getWasm()), importObj);
{
const obj = new Exports();
obj.addToImports(importObj);
obj.instantiate(new WebAssembly.Instance(new WebAssembly.Module(getWasm()), importObj));
}
}
await run()

View File

@@ -0,0 +1,3 @@
#include <exports.h>
void exports_nop() {}

View File

@@ -0,0 +1,13 @@
test-imports: func()
allocated-bytes: func() -> u32
list-param: func(a: list<u8>)
list-param2: func(a: string)
list-param3: func(a: list<string>)
list-param4: func(a: list<list<string>>)
list-result: func() -> list<u8>
list-result2: func() -> string
list-result3: func() -> list<string>
list-roundtrip: func(a: list<u8>) -> list<u8>
string-roundtrip: func(a: string) -> string

View File

@@ -0,0 +1,109 @@
from exports.bindings import Exports
from imports.bindings import add_imports_to_linker, Imports
from typing import Tuple, List
import exports.bindings as e
import imports.bindings as i
import sys
import wasmtime
class MyImports:
def list_param(self, a: bytes) -> None:
assert(a == b'\x01\x02\x03\x04')
def list_param2(self, a: str) -> None:
assert(a == 'foo')
def list_param3(self, a: List[str]) -> None:
assert(a == ['foo', 'bar', 'baz'])
def list_param4(self, a: List[List[str]]) -> None:
assert(a == [['foo', 'bar'], ['baz']])
def list_result(self) -> bytes:
return b'\x01\x02\x03\x04\x05'
def list_result2(self) -> str:
return 'hello!'
def list_result3(self) -> List[str]:
return ['hello,', 'world!']
def list_roundtrip(self, a: bytes) -> bytes:
return a
def string_roundtrip(self, a: str) -> str:
return a
def unaligned_roundtrip1(self, a: List[int], b: List[int], c: List[int], d: List[i.Flag32], e: List[i.Flag64]) -> None:
assert(a == [1])
assert(b == [2])
assert(c == [3])
assert(d == [i.Flag32.B8])
assert(e == [i.Flag64.B9])
def unaligned_roundtrip2(self, a: List[i.UnalignedRecord], b: List[float], c: List[float], d: List[str], e: List[bytes]) -> None:
assert(a == [i.UnalignedRecord(a=10, b=11)])
assert(b == [100.0])
assert(c == [101.0])
assert(d == ['foo'])
assert(e == [b'\x66'])
def list_minmax8(self, a: bytes, b: List[int]) -> Tuple[bytes, List[int]]:
assert(a == b'\x00\xff')
assert(b == [-(1 << (8 - 1)), (1 << (8 - 1)) - 1])
return (a, b)
def list_minmax16(self, a: List[int], b: List[int]) -> Tuple[List[int], List[int]]:
assert(a == [0, (1 << 16) - 1])
assert(b == [-(1 << (16 - 1)), (1 << (16 - 1)) - 1])
return (a, b)
def list_minmax32(self, a: List[int], b: List[int]) -> Tuple[List[int], List[int]]:
assert(a == [0, (1 << 32) - 1])
assert(b == [-(1 << (32 - 1)), (1 << (32 - 1)) - 1])
return (a, b)
def list_minmax64(self, a: List[int], b: List[int]) -> Tuple[List[int], List[int]]:
assert(a == [0, (1 << 64) - 1])
assert(b == [-(1 << (64 - 1)), (1 << (64 - 1)) - 1])
return (a, b)
def list_minmax_float(self, a: List[float], b: List[float]) -> Tuple[List[float], List[float]]:
assert(a == [-3.4028234663852886e+38, 3.4028234663852886e+38, -float('inf'), float('inf')])
assert(b == [-sys.float_info.max, sys.float_info.max, -float('inf'), float('inf')])
return (a, b)
def run(wasm_file: str) -> None:
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, wasm_file)
linker = wasmtime.Linker(store.engine)
linker.define_wasi()
wasi = wasmtime.WasiConfig()
wasi.inherit_stdout()
wasi.inherit_stderr()
store.set_wasi(wasi)
imports = MyImports()
add_imports_to_linker(linker, store, imports)
wasm = Exports(store, linker, module)
allocated_bytes = wasm.allocated_bytes(store)
wasm.test_imports(store)
wasm.list_param(store, b'\x01\x02\x03\x04')
wasm.list_param2(store, "foo")
wasm.list_param3(store, ["foo", "bar", "baz"])
wasm.list_param4(store, [["foo", "bar"], ["baz"]])
assert(wasm.list_result(store) == b'\x01\x02\x03\x04\x05')
assert(wasm.list_result2(store) == "hello!")
assert(wasm.list_result3(store) == ["hello,", "world!"])
assert(wasm.string_roundtrip(store, "x") == "x")
assert(wasm.string_roundtrip(store, "") == "")
assert(wasm.string_roundtrip(store, "hello ⚑ world") == "hello ⚑ world")
# Ensure that we properly called `free` everywhere in all the glue that we
# needed to.
assert(allocated_bytes == wasm.allocated_bytes(store))
if __name__ == '__main__':
run(sys.argv[1])

View File

@@ -0,0 +1,159 @@
use anyhow::Result;
wit_bindgen_wasmtime::export!("../../tests/runtime/lists/imports.wit");
use imports::*;
use wit_bindgen_wasmtime::Le;
#[derive(Default)]
pub struct MyImports;
impl Imports for MyImports {
fn list_param(&mut self, list: &[u8]) {
assert_eq!(list, [1, 2, 3, 4]);
}
fn list_param2(&mut self, ptr: &str) {
assert_eq!(ptr, "foo");
}
fn list_param3(&mut self, ptr: Vec<&str>) {
assert_eq!(ptr.len(), 3);
assert_eq!(ptr[0], "foo");
assert_eq!(ptr[1], "bar");
assert_eq!(ptr[2], "baz");
}
fn list_param4(&mut self, ptr: Vec<Vec<&str>>) {
assert_eq!(ptr.len(), 2);
assert_eq!(ptr[0][0], "foo");
assert_eq!(ptr[0][1], "bar");
assert_eq!(ptr[1][0], "baz");
}
fn list_result(&mut self) -> Vec<u8> {
vec![1, 2, 3, 4, 5]
}
fn list_result2(&mut self) -> String {
"hello!".to_string()
}
fn list_result3(&mut self) -> Vec<String> {
vec!["hello,".to_string(), "world!".to_string()]
}
fn list_roundtrip(&mut self, list: &[u8]) -> Vec<u8> {
list.to_vec()
}
fn string_roundtrip(&mut self, s: &str) -> String {
s.to_string()
}
fn list_minmax8(&mut self, u: &[u8], s: &[i8]) -> (Vec<u8>, Vec<i8>) {
assert_eq!(u, [u8::MIN, u8::MAX]);
assert_eq!(s, [i8::MIN, i8::MAX]);
(u.to_vec(), s.to_vec())
}
fn list_minmax16(&mut self, u: &[Le<u16>], s: &[Le<i16>]) -> (Vec<u16>, Vec<i16>) {
assert_eq!(u, [u16::MIN, u16::MAX]);
assert_eq!(s, [i16::MIN, i16::MAX]);
(
u.iter().map(|e| e.get()).collect(),
s.iter().map(|e| e.get()).collect(),
)
}
fn list_minmax32(&mut self, u: &[Le<u32>], s: &[Le<i32>]) -> (Vec<u32>, Vec<i32>) {
assert_eq!(u, [u32::MIN, u32::MAX]);
assert_eq!(s, [i32::MIN, i32::MAX]);
(
u.iter().map(|e| e.get()).collect(),
s.iter().map(|e| e.get()).collect(),
)
}
fn list_minmax64(&mut self, u: &[Le<u64>], s: &[Le<i64>]) -> (Vec<u64>, Vec<i64>) {
assert_eq!(u, [u64::MIN, u64::MAX]);
assert_eq!(s, [i64::MIN, i64::MAX]);
(
u.iter().map(|e| e.get()).collect(),
s.iter().map(|e| e.get()).collect(),
)
}
fn list_minmax_float(&mut self, u: &[Le<f32>], s: &[Le<f64>]) -> (Vec<f32>, Vec<f64>) {
assert_eq!(u, [f32::MIN, f32::MAX, f32::NEG_INFINITY, f32::INFINITY]);
assert_eq!(s, [f64::MIN, f64::MAX, f64::NEG_INFINITY, f64::INFINITY]);
(
u.iter().map(|e| e.get()).collect(),
s.iter().map(|e| e.get()).collect(),
)
}
fn unaligned_roundtrip1(
&mut self,
u16s: &[Le<u16>],
u32s: &[Le<u32>],
u64s: &[Le<u64>],
flag32s: Vec<Flag32>,
flag64s: Vec<Flag64>,
) {
assert_eq!(u16s, [1]);
assert_eq!(u32s, [2]);
assert_eq!(u64s, [3]);
assert_eq!(flag32s, [Flag32::B8]);
assert_eq!(flag64s, [Flag64::B9]);
}
fn unaligned_roundtrip2(
&mut self,
records: &[Le<UnalignedRecord>],
f32s: &[Le<f32>],
f64s: &[Le<f64>],
strings: Vec<&str>,
lists: Vec<&[u8]>,
) {
assert_eq!(records.len(), 1);
assert_eq!(records[0].get().a, 10);
assert_eq!(records[0].get().b, 11);
assert_eq!(f32s, [100.0]);
assert_eq!(f64s, [101.0]);
assert_eq!(strings, ["foo"]);
assert_eq!(lists, [&[102][..]]);
}
}
wit_bindgen_wasmtime::import!("../../tests/runtime/lists/exports.wit");
fn run(wasm: &str) -> Result<()> {
use exports::*;
let (exports, mut store) = crate::instantiate(
wasm,
|linker| imports::add_to_linker(linker, |cx| -> &mut MyImports { &mut cx.imports }),
|store, module, linker| Exports::instantiate(store, module, linker, |cx| &mut cx.exports),
)?;
let bytes = exports.allocated_bytes(&mut store)?;
exports.test_imports(&mut store)?;
exports.list_param(&mut store, &[1, 2, 3, 4])?;
exports.list_param2(&mut store, "foo")?;
exports.list_param3(&mut store, &["foo", "bar", "baz"])?;
exports.list_param4(&mut store, &[&["foo", "bar"], &["baz"]])?;
assert_eq!(exports.list_result(&mut store)?, [1, 2, 3, 4, 5]);
assert_eq!(exports.list_result2(&mut store)?, "hello!");
assert_eq!(exports.list_result3(&mut store)?, ["hello,", "world!"]);
assert_eq!(exports.string_roundtrip(&mut store, "x")?, "x");
assert_eq!(exports.string_roundtrip(&mut store, "")?, "");
assert_eq!(
exports.string_roundtrip(&mut store, "hello ⚑ world")?,
"hello ⚑ world"
);
// Ensure that we properly called `free` everywhere in all the glue that we
// needed to.
assert_eq!(bytes, exports.allocated_bytes(&mut store)?);
Ok(())
}

View File

@@ -0,0 +1,139 @@
import { addImportsToImports, Imports, FLAG32_B8, FLAG64_B9 } from "./imports.js";
import { Exports } from "./exports.js";
import * as exports from "./exports.js";
import { getWasm, addWasiToImports } from "./helpers.js";
// @ts-ignore
import * as assert from 'assert';
async function run() {
const importObj = {};
const imports: Imports = {
listParam(a) {
assert.deepStrictEqual(Array.from(a), [1, 2, 3, 4]);
},
listParam2(a) {
assert.strictEqual(a, 'foo');
},
listParam3(a) {
assert.deepStrictEqual(a, ['foo', 'bar', 'baz']);
},
listParam4(a) {
assert.deepStrictEqual(a, [['foo', 'bar'], ['baz']]);
},
listResult() {
return new Uint8Array([1, 2, 3, 4, 5]);
},
listResult2() { return 'hello!'; },
listResult3() { return ['hello,', 'world!']; },
listRoundtrip(x) { return x; },
stringRoundtrip(x) { return x; },
unalignedRoundtrip1(u16, u32, u64, flag32, flag64) {
assert.deepStrictEqual(Array.from(u16), [1]);
assert.deepStrictEqual(Array.from(u32), [2]);
assert.deepStrictEqual(Array.from(u64), [3n]);
assert.deepStrictEqual(flag32, [FLAG32_B8]);
assert.deepStrictEqual(flag64, [FLAG64_B9]);
},
unalignedRoundtrip2(record, f32, f64, string, list) {
assert.deepStrictEqual(Array.from(record), [{ a: 10, b: 11n }]);
assert.deepStrictEqual(Array.from(f32), [100]);
assert.deepStrictEqual(Array.from(f64), [101]);
assert.deepStrictEqual(string, ['foo']);
assert.deepStrictEqual(list, [new Uint8Array([102])]);
},
listMinmax8(u, s) {
assert.deepEqual(u.length, 2);
assert.deepEqual(u[0], 0);
assert.deepEqual(u[1], (1 << 8) - 1);
assert.deepEqual(s.length, 2);
assert.deepEqual(s[0], -(1 << 7));
assert.deepEqual(s[1], (1 << 7) - 1);
return [u, s];
},
listMinmax16(u, s) {
assert.deepEqual(u.length, 2);
assert.deepEqual(u[0], 0);
assert.deepEqual(u[1], (1 << 16) - 1);
assert.deepEqual(s.length, 2);
assert.deepEqual(s[0], -(1 << 15));
assert.deepEqual(s[1], (1 << 15) - 1);
return [u, s];
},
listMinmax32(u, s) {
assert.deepEqual(u.length, 2);
assert.deepEqual(u[0], 0);
assert.deepEqual(u[1], ~0 >>> 0);
assert.deepEqual(s.length, 2);
assert.deepEqual(s[0], 1 << 31);
assert.deepEqual(s[1], ((1 << 31) - 1) >>> 0);
return [u, s];
},
listMinmax64(u, s) {
assert.deepEqual(u.length, 2);
assert.deepEqual(u[0], 0n);
assert.deepEqual(u[1], (2n ** 64n) - 1n);
assert.deepEqual(s.length, 2);
assert.deepEqual(s[0], -(2n ** 63n));
assert.deepEqual(s[1], (2n ** 63n) - 1n);
return [u, s];
},
listMinmaxFloat(f, d) {
assert.deepEqual(f.length, 4);
assert.deepEqual(f[0], -3.4028234663852886e+38);
assert.deepEqual(f[1], 3.4028234663852886e+38);
assert.deepEqual(f[2], Number.NEGATIVE_INFINITY);
assert.deepEqual(f[3], Number.POSITIVE_INFINITY);
assert.deepEqual(d.length, 4);
assert.deepEqual(d[0], -Number.MAX_VALUE);
assert.deepEqual(d[1], Number.MAX_VALUE);
assert.deepEqual(d[2], Number.NEGATIVE_INFINITY);
assert.deepEqual(d[3], Number.POSITIVE_INFINITY);
return [f, d];
},
};
let instance: WebAssembly.Instance;
addImportsToImports(importObj, imports, name => instance.exports[name]);
const wasi = addWasiToImports(importObj);
const wasm = new Exports();
await wasm.instantiate(getWasm(), importObj);
wasi.start(wasm.instance);
instance = wasm.instance;
const bytes = wasm.allocatedBytes();
wasm.testImports();
wasm.listParam(new Uint8Array([1, 2, 3, 4]));
wasm.listParam2("foo");
wasm.listParam3(["foo", "bar", "baz"]);
wasm.listParam4([["foo", "bar"], ["baz"]]);
assert.deepStrictEqual(Array.from(wasm.listResult()), [1, 2, 3, 4, 5]);
assert.deepStrictEqual(wasm.listResult2(), "hello!");
assert.deepStrictEqual(wasm.listResult3(), ["hello,", "world!"]);
const buffer = new ArrayBuffer(8);
(new Uint8Array(buffer)).set(new Uint8Array([1, 2, 3, 4]), 2);
// Create a view of the four bytes in the middle of the buffer
const view = new Uint8Array(buffer, 2, 4);
assert.deepStrictEqual(Array.from(wasm.listRoundtrip(view)), [1, 2, 3, 4]);
assert.deepStrictEqual(wasm.stringRoundtrip("x"), "x");
assert.deepStrictEqual(wasm.stringRoundtrip(""), "");
assert.deepStrictEqual(wasm.stringRoundtrip("hello ⚑ world"), "hello ⚑ world");
// Ensure that we properly called `free` everywhere in all the glue that we
// needed to.
assert.strictEqual(bytes, wasm.allocatedBytes());
}
await run()

View File

@@ -0,0 +1,40 @@
flags flag32 {
b0, b1, b2, b3, b4, b5, b6, b7,
b8, b9, b10, b11, b12, b13, b14, b15,
b16, b17, b18, b19, b20, b21, b22, b23,
b24, b25, b26, b27, b28, b29, b30, b31,
}
flags flag64 {
b0, b1, b2, b3, b4, b5, b6, b7,
b8, b9, b10, b11, b12, b13, b14, b15,
b16, b17, b18, b19, b20, b21, b22, b23,
b24, b25, b26, b27, b28, b29, b30, b31,
b32, b33, b34, b35, b36, b37, b38, b39,
b40, b41, b42, b43, b44, b45, b46, b47,
b48, b49, b50, b51, b52, b53, b54, b55,
b56, b57, b58, b59, b60, b61, b62, b63,
}
list-param: func(a: list<u8>)
list-param2: func(a: string)
list-param3: func(a: list<string>)
list-param4: func(a: list<list<string>>)
list-result: func() -> list<u8>
list-result2: func() -> string
list-result3: func() -> list<string>
list-minmax8: func(a: list<u8>, b: list<s8>) -> tuple<list<u8>, list<s8>>
list-minmax16: func(a: list<u16>, b: list<s16>) -> tuple<list<u16>, list<s16>>
list-minmax32: func(a: list<u32>, b: list<s32>) -> tuple<list<u32>, list<s32>>
list-minmax64: func(a: list<u64>, b: list<s64>) -> tuple<list<u64>, list<s64>>
list-minmax-float: func(a: list<float32>, b: list<float64>) -> tuple<list<float32>, list<float64>>
list-roundtrip: func(a: list<u8>) -> list<u8>
string-roundtrip: func(a: string) -> string
unaligned-roundtrip1: func(a: list<u16>, b: list<u32>, c: list<u64>, d: list<flag32>, e: list<flag64>)
record unaligned-record { a: u32, b: u64 }
unaligned-roundtrip2: func(a: list<unaligned-record>, b: list<float32>, c: list<float64>, d: list<string>, e: list<list<u8>>)

View File

@@ -0,0 +1,292 @@
#include <assert.h>
#include <exports.h>
#include <float.h>
#include <imports.h>
#include <limits.h>
#include <math.h>
#include <stdalign.h>
#include <stdlib.h>
#include <string.h>
// "custom allocator" which just keeps track of allocated bytes
static size_t ALLOCATED_BYTES = 0;
__attribute__((export_name("canonical_abi_realloc")))
void *canonical_abi_realloc( void *ptr, size_t orig_size, size_t orig_align, size_t new_size) {
void *ret = realloc(ptr, new_size);
if (!ret)
abort();
ALLOCATED_BYTES -= orig_size;
ALLOCATED_BYTES += new_size;
return ret;
}
__attribute__((export_name("canonical_abi_free")))
void canonical_abi_free(void *ptr, size_t size, size_t align) {
if (size > 0) {
ALLOCATED_BYTES -= size;
free(ptr);
}
}
uint32_t exports_allocated_bytes(void) {
return ALLOCATED_BYTES;
}
void exports_test_imports() {
{
uint8_t list[] = {1, 2, 3, 4};
imports_list_u8_t a;
a.ptr = list;
a.len = 4;
imports_list_param(&a);
}
{
imports_string_t a;
imports_string_set(&a, "foo");
imports_list_param2(&a);
}
{
imports_string_t list[3];
imports_string_set(&list[0], "foo");
imports_string_set(&list[1], "bar");
imports_string_set(&list[2], "baz");
imports_list_string_t a;
a.ptr = list;
a.len = 3;
imports_list_param3(&a);
}
{
imports_string_t list1[2];
imports_string_t list2[1];
imports_string_set(&list1[0], "foo");
imports_string_set(&list1[1], "bar");
imports_string_set(&list2[0], "baz");
imports_list_list_string_t a;
a.ptr[0].len = 2;
a.ptr[0].ptr = list1;
a.ptr[1].len = 1;
a.ptr[1].ptr = list2;
a.len = 2;
imports_list_param4(&a);
}
{
imports_list_u8_t a;
imports_list_result(&a);
assert(a.len == 5);
assert(memcmp(a.ptr, "\x01\x02\x03\x04\x05", 5) == 0);
imports_list_u8_free(&a);
}
{
imports_string_t a;
imports_list_result2(&a);
assert(a.len == 6);
assert(memcmp(a.ptr, "hello!", 6) == 0);
imports_string_free(&a);
}
{
imports_list_string_t a;
imports_list_result3(&a);
assert(a.len == 2);
assert(a.ptr[0].len == 6);
assert(a.ptr[1].len == 6);
assert(memcmp(a.ptr[0].ptr, "hello,", 6) == 0);
assert(memcmp(a.ptr[1].ptr, "world!", 6) == 0);
imports_list_string_free(&a);
}
{
imports_string_t a, b;
imports_string_set(&a, "x");
imports_string_roundtrip(&a, &b);
assert(b.len == a.len);
assert(memcmp(b.ptr, a.ptr, a.len) == 0);
imports_string_free(&b);
imports_string_set(&a, "");
imports_string_roundtrip(&a, &b);
assert(b.len == a.len);
imports_string_free(&b);
imports_string_set(&a, "hello");
imports_string_roundtrip(&a, &b);
assert(b.len == a.len);
assert(memcmp(b.ptr, a.ptr, a.len) == 0);
imports_string_free(&b);
imports_string_set(&a, "hello ⚑ world");
imports_string_roundtrip(&a, &b);
assert(b.len == a.len);
assert(memcmp(b.ptr, a.ptr, a.len) == 0);
imports_string_free(&b);
}
{
uint8_t u8[2] = {0, UCHAR_MAX};
int8_t s8[2] = {SCHAR_MIN, SCHAR_MAX};
imports_list_u8_t list_u8 = { u8, 2 };
imports_list_s8_t list_s8 = { s8, 2 };
imports_list_u8_t list_u8_out;
imports_list_s8_t list_s8_out;
imports_list_minmax8(&list_u8, &list_s8, &list_u8_out, &list_s8_out);
assert(list_u8_out.len == 2 && list_u8_out.ptr[0] == 0 && list_u8_out.ptr[1] == UCHAR_MAX);
assert(list_s8_out.len == 2 && list_s8_out.ptr[0] == SCHAR_MIN && list_s8_out.ptr[1] == SCHAR_MAX);
imports_list_u8_free(&list_u8_out);
imports_list_s8_free(&list_s8_out);
}
{
uint16_t u16[2] = {0, USHRT_MAX};
int16_t s16[2] = {SHRT_MIN, SHRT_MAX};
imports_list_u16_t list_u16 = { u16, 2 };
imports_list_s16_t list_s16 = { s16, 2 };
imports_list_u16_t list_u16_out;
imports_list_s16_t list_s16_out;
imports_list_minmax16(&list_u16, &list_s16, &list_u16_out, &list_s16_out);
assert(list_u16_out.len == 2 && list_u16_out.ptr[0] == 0 && list_u16_out.ptr[1] == USHRT_MAX);
assert(list_s16_out.len == 2 && list_s16_out.ptr[0] == SHRT_MIN && list_s16_out.ptr[1] == SHRT_MAX);
imports_list_u16_free(&list_u16_out);
imports_list_s16_free(&list_s16_out);
}
{
uint32_t u32[2] = {0, UINT_MAX};
int32_t s32[2] = {INT_MIN, INT_MAX};
imports_list_u32_t list_u32 = { u32, 2 };
imports_list_s32_t list_s32 = { s32, 2 };
imports_list_u32_t list_u32_out;
imports_list_s32_t list_s32_out;
imports_list_minmax32(&list_u32, &list_s32, &list_u32_out, &list_s32_out);
assert(list_u32_out.len == 2 && list_u32_out.ptr[0] == 0 && list_u32_out.ptr[1] == UINT_MAX);
assert(list_s32_out.len == 2 && list_s32_out.ptr[0] == INT_MIN && list_s32_out.ptr[1] == INT_MAX);
imports_list_u32_free(&list_u32_out);
imports_list_s32_free(&list_s32_out);
}
{
uint64_t u64[2] = {0, ULLONG_MAX};
int64_t s64[2] = {LLONG_MIN, LLONG_MAX};
imports_list_u64_t list_u64 = { u64, 2 };
imports_list_s64_t list_s64 = { s64, 2 };
imports_list_u64_t list_u64_out;
imports_list_s64_t list_s64_out;
imports_list_minmax64(&list_u64, &list_s64, &list_u64_out, &list_s64_out);
assert(list_u64_out.len == 2 && list_u64_out.ptr[0] == 0 && list_u64_out.ptr[1] == ULLONG_MAX);
assert(list_s64_out.len == 2 && list_s64_out.ptr[0] == LLONG_MIN && list_s64_out.ptr[1] == LLONG_MAX);
imports_list_u64_free(&list_u64_out);
imports_list_s64_free(&list_s64_out);
}
{
float f32[4] = {-FLT_MAX, FLT_MAX, -INFINITY, INFINITY};
double f64[4] = {-DBL_MAX, DBL_MAX, -INFINITY, INFINITY};
imports_list_float32_t list_float32 = { f32, 4 };
imports_list_float64_t list_float64 = { f64, 4 };
imports_list_float32_t list_float32_out;
imports_list_float64_t list_float64_out;
imports_list_minmax_float(&list_float32, &list_float64, &list_float32_out, &list_float64_out);
assert(list_float32_out.len == 4 && list_float32_out.ptr[0] == -FLT_MAX && list_float32_out.ptr[1] == FLT_MAX);
assert(list_float32_out.ptr[2] == -INFINITY && list_float32_out.ptr[3] == INFINITY);
assert(list_float64_out.len == 4 && list_float64_out.ptr[0] == -DBL_MAX && list_float64_out.ptr[1] == DBL_MAX);
assert(list_float64_out.ptr[2] == -INFINITY && list_float64_out.ptr[3] == INFINITY);
imports_list_float32_free(&list_float32_out);
imports_list_float64_free(&list_float64_out);
}
}
void exports_list_param(exports_list_u8_t *a) {
assert(a->len == 4);
assert(a->ptr[0] == 1);
assert(a->ptr[1] == 2);
assert(a->ptr[2] == 3);
assert(a->ptr[3] == 4);
exports_list_u8_free(a);
}
void exports_list_param2(exports_string_t *a) {
assert(a->len == 3);
assert(a->ptr[0] == 'f');
assert(a->ptr[1] == 'o');
assert(a->ptr[2] == 'o');
exports_string_free(a);
}
void exports_list_param3(exports_list_string_t *a) {
assert(a->len == 3);
assert(a->ptr[0].len == 3);
assert(a->ptr[0].ptr[0] == 'f');
assert(a->ptr[0].ptr[1] == 'o');
assert(a->ptr[0].ptr[2] == 'o');
assert(a->ptr[1].len == 3);
assert(a->ptr[1].ptr[0] == 'b');
assert(a->ptr[1].ptr[1] == 'a');
assert(a->ptr[1].ptr[2] == 'r');
assert(a->ptr[2].len == 3);
assert(a->ptr[2].ptr[0] == 'b');
assert(a->ptr[2].ptr[1] == 'a');
assert(a->ptr[2].ptr[2] == 'z');
exports_list_string_free(a);
}
void exports_list_param4(exports_list_list_string_t *a) {
assert(a->len == 2);
assert(a->ptr[0].len == 2);
assert(a->ptr[1].len == 1);
assert(a->ptr[0].ptr[0].len == 3);
assert(a->ptr[0].ptr[0].ptr[0] == 'f');
assert(a->ptr[0].ptr[0].ptr[1] == 'o');
assert(a->ptr[0].ptr[0].ptr[2] == 'o');
assert(a->ptr[0].ptr[1].len == 3);
assert(a->ptr[0].ptr[1].ptr[0] == 'b');
assert(a->ptr[0].ptr[1].ptr[1] == 'a');
assert(a->ptr[0].ptr[1].ptr[2] == 'r');
assert(a->ptr[1].ptr[0].len == 3);
assert(a->ptr[1].ptr[0].ptr[0] == 'b');
assert(a->ptr[1].ptr[0].ptr[1] == 'a');
assert(a->ptr[1].ptr[0].ptr[2] == 'z');
exports_list_list_string_free(a);
}
void exports_list_result(exports_list_u8_t *ret0) {
ret0->ptr = canonical_abi_realloc(NULL, 0, 1, 5);
ret0->len = 5;
ret0->ptr[0] = 1;
ret0->ptr[1] = 2;
ret0->ptr[2] = 3;
ret0->ptr[3] = 4;
ret0->ptr[4] = 5;
}
void exports_list_result2(exports_string_t *ret0) {
exports_string_dup(ret0, "hello!");
}
void exports_list_result3(exports_list_string_t *ret0) {
ret0->len = 2;
ret0->ptr = canonical_abi_realloc(NULL, 0, alignof(exports_string_t), 2 * sizeof(exports_string_t));
exports_string_dup(&ret0->ptr[0], "hello,");
exports_string_dup(&ret0->ptr[1], "world!");
}
void exports_list_roundtrip(exports_list_u8_t *a, exports_list_u8_t *ret0) {
*ret0 = *a;
}
void exports_string_roundtrip(exports_string_t *a, exports_string_t *ret0) {
*ret0 = *a;
}

View File

@@ -0,0 +1,169 @@
wit_bindgen_rust::import!("../../tests/runtime/lists/imports.wit");
wit_bindgen_rust::export!("../../tests/runtime/lists/exports.wit");
use std::alloc::{self, Layout};
use std::mem;
use std::ptr;
struct Exports;
impl exports::Exports for Exports {
fn allocated_bytes() -> u32 {
test_rust_wasm::get() as u32
}
fn test_imports() {
use imports::*;
let _guard = test_rust_wasm::guard();
list_param(&[1, 2, 3, 4]);
list_param2("foo");
list_param3(&["foo", "bar", "baz"]);
list_param4(&[&["foo", "bar"], &["baz"]]);
assert_eq!(list_result(), [1, 2, 3, 4, 5]);
assert_eq!(list_result2(), "hello!");
assert_eq!(list_result3(), ["hello,", "world!"]);
assert_eq!(string_roundtrip("x"), "x");
assert_eq!(string_roundtrip(""), "");
assert_eq!(string_roundtrip("hello"), "hello");
assert_eq!(string_roundtrip("hello ⚑ world"), "hello ⚑ world");
struct Unaligned<T: Copy> {
alloc: *mut u8,
_marker: std::marker::PhantomData<T>,
}
impl<T: Copy> Unaligned<T> {
fn layout() -> Layout {
Layout::from_size_align(2 * mem::size_of::<T>(), 8).unwrap()
}
fn new(data: T) -> Unaligned<T> {
unsafe {
let alloc = alloc::alloc(Self::layout());
assert!(!alloc.is_null());
ptr::write_unaligned(alloc.add(1).cast(), data);
Unaligned {
alloc,
_marker: Default::default(),
}
}
}
fn as_slice(&self) -> *const [T] {
unsafe { ptr::slice_from_raw_parts(self.alloc.add(1).cast(), 1) }
}
}
impl<T: Copy> Drop for Unaligned<T> {
fn drop(&mut self) {
unsafe {
alloc::dealloc(self.alloc, Self::layout());
}
}
}
unsafe {
let u16s = Unaligned::new(1);
let u32s = Unaligned::new(2);
let u64s = Unaligned::new(3);
let flag32s = Unaligned::new(Flag32::B8);
let flag64s = Unaligned::new(Flag64::B9);
let records = Unaligned::new(UnalignedRecord { a: 10, b: 11 });
let f32s = Unaligned::new(100.0);
let f64s = Unaligned::new(101.0);
let strings = Unaligned::new("foo");
let lists = Unaligned::new(&[102][..]);
// Technically this is UB because we're creating safe slices from
// unaligned pointers, but we're hoping that because we're just passing
// off pointers to an import through a safe import we can get away with
// this. If this ever becomes a problem we'll just need to call the raw
// import with raw integers.
unaligned_roundtrip1(
&*u16s.as_slice(),
&*u32s.as_slice(),
&*u64s.as_slice(),
&*flag32s.as_slice(),
&*flag64s.as_slice(),
);
unaligned_roundtrip2(
&*records.as_slice(),
&*f32s.as_slice(),
&*f64s.as_slice(),
&*strings.as_slice(),
&*lists.as_slice(),
);
}
assert_eq!(
list_minmax8(&[u8::MIN, u8::MAX], &[i8::MIN, i8::MAX]),
(vec![u8::MIN, u8::MAX], vec![i8::MIN, i8::MAX]),
);
assert_eq!(
list_minmax16(&[u16::MIN, u16::MAX], &[i16::MIN, i16::MAX]),
(vec![u16::MIN, u16::MAX], vec![i16::MIN, i16::MAX]),
);
assert_eq!(
list_minmax32(&[u32::MIN, u32::MAX], &[i32::MIN, i32::MAX]),
(vec![u32::MIN, u32::MAX], vec![i32::MIN, i32::MAX]),
);
assert_eq!(
list_minmax64(&[u64::MIN, u64::MAX], &[i64::MIN, i64::MAX]),
(vec![u64::MIN, u64::MAX], vec![i64::MIN, i64::MAX]),
);
assert_eq!(
list_minmax_float(
&[f32::MIN, f32::MAX, f32::NEG_INFINITY, f32::INFINITY],
&[f64::MIN, f64::MAX, f64::NEG_INFINITY, f64::INFINITY]
),
(
vec![f32::MIN, f32::MAX, f32::NEG_INFINITY, f32::INFINITY],
vec![f64::MIN, f64::MAX, f64::NEG_INFINITY, f64::INFINITY],
),
);
}
fn list_param(list: Vec<u8>) {
assert_eq!(list, [1, 2, 3, 4]);
}
fn list_param2(ptr: String) {
assert_eq!(ptr, "foo");
}
fn list_param3(ptr: Vec<String>) {
assert_eq!(ptr.len(), 3);
assert_eq!(ptr[0], "foo");
assert_eq!(ptr[1], "bar");
assert_eq!(ptr[2], "baz");
}
fn list_param4(ptr: Vec<Vec<String>>) {
assert_eq!(ptr.len(), 2);
assert_eq!(ptr[0][0], "foo");
assert_eq!(ptr[0][1], "bar");
assert_eq!(ptr[1][0], "baz");
}
fn list_result() -> Vec<u8> {
vec![1, 2, 3, 4, 5]
}
fn list_result2() -> String {
"hello!".to_string()
}
fn list_result3() -> Vec<String> {
vec!["hello,".to_string(), "world!".to_string()]
}
fn list_roundtrip(x: Vec<u8>) -> Vec<u8> {
x.clone()
}
fn string_roundtrip(x: String) -> String {
x.clone()
}
}

View File

@@ -0,0 +1,22 @@
many-arguments: func(
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
a8: u64,
a9: u64,
a10: u64,
a11: u64,
a12: u64,
a13: u64,
a14: u64,
a15: u64,
a16: u64,
a17: u64,
a18: u64,
a19: u64,
a20: u64,
)

View File

@@ -0,0 +1,68 @@
from exports.bindings import Exports
from imports.bindings import add_imports_to_linker, Imports
import math;
import sys
import wasmtime
class MyImports:
def many_arguments(self,
a1: int,
a2: int,
a3: int,
a4: int,
a5: int,
a6: int,
a7: int,
a8: int,
a9: int,
a10: int,
a11: int,
a12: int,
a13: int,
a14: int,
a15: int,
a16: int,
a17: int,
a18: int,
a19: int,
a20: int) -> None:
assert(a1 == 1)
assert(a2 == 2)
assert(a3 == 3)
assert(a4 == 4)
assert(a5 == 5)
assert(a6 == 6)
assert(a7 == 7)
assert(a8 == 8)
assert(a9 == 9)
assert(a10 == 10)
assert(a11 == 11)
assert(a12 == 12)
assert(a13 == 13)
assert(a14 == 14)
assert(a15 == 15)
assert(a16 == 16)
assert(a17 == 17)
assert(a18 == 18)
assert(a19 == 19)
assert(a20 == 20)
def run(wasm_file: str) -> None:
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, wasm_file)
linker = wasmtime.Linker(store.engine)
linker.define_wasi()
wasi = wasmtime.WasiConfig()
wasi.inherit_stdout()
wasi.inherit_stderr()
store.set_wasi(wasi)
imports = MyImports()
add_imports_to_linker(linker, store, imports)
wasm = Exports(store, linker, module)
wasm.many_arguments(store, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,14, 15, 16, 17, 18, 19, 20)
if __name__ == '__main__':
run(sys.argv[1])

View File

@@ -0,0 +1,71 @@
use anyhow::Result;
wit_bindgen_wasmtime::export!("../../tests/runtime/many_arguments/imports.wit");
#[derive(Default)]
pub struct MyImports {}
impl imports::Imports for MyImports {
fn many_arguments(
&mut self,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
a8: u64,
a9: u64,
a10: u64,
a11: u64,
a12: u64,
a13: u64,
a14: u64,
a15: u64,
a16: u64,
a17: u64,
a18: u64,
a19: u64,
a20: u64,
) {
assert_eq!(a1, 1);
assert_eq!(a2, 2);
assert_eq!(a3, 3);
assert_eq!(a4, 4);
assert_eq!(a5, 5);
assert_eq!(a6, 6);
assert_eq!(a7, 7);
assert_eq!(a8, 8);
assert_eq!(a9, 9);
assert_eq!(a10, 10);
assert_eq!(a11, 11);
assert_eq!(a12, 12);
assert_eq!(a13, 13);
assert_eq!(a14, 14);
assert_eq!(a15, 15);
assert_eq!(a16, 16);
assert_eq!(a17, 17);
assert_eq!(a18, 18);
assert_eq!(a19, 19);
assert_eq!(a20, 20);
}
}
wit_bindgen_wasmtime::import!("../../tests/runtime/many_arguments/exports.wit");
fn run(wasm: &str) -> Result<()> {
let (exports, mut store) = crate::instantiate(
wasm,
|linker| imports::add_to_linker(linker, |cx| -> &mut MyImports { &mut cx.imports }),
|store, module, linker| {
exports::Exports::instantiate(store, module, linker, |cx| &mut cx.exports)
},
)?;
exports.many_arguments(
&mut store, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
)?;
Ok(())
}

View File

@@ -0,0 +1,95 @@
import { addImportsToImports, Imports } from "./imports.js";
import { Exports } from "./exports.js";
import { getWasm, addWasiToImports } from "./helpers.js";
function assertEq(x: any, y: any) {
if (x !== y)
throw new Error(`${x} != ${y}`);
}
function assert(x: boolean) {
if (!x)
throw new Error("assert failed");
}
async function run() {
const importObj = {};
const imports: Imports = {
manyArguments(
a1,
a2,
a3,
a4,
a5,
a6,
a7,
a8,
a9,
a10,
a11,
a12,
a13,
a14,
a15,
a16,
a17,
a18,
a19,
a20,
) {
assertEq(a1, 1n);
assertEq(a2, 2n);
assertEq(a3, 3n);
assertEq(a4, 4n);
assertEq(a5, 5n);
assertEq(a6, 6n);
assertEq(a7, 7n);
assertEq(a8, 8n);
assertEq(a9, 9n);
assertEq(a10, 10n);
assertEq(a11, 11n);
assertEq(a12, 12n);
assertEq(a13, 13n);
assertEq(a14, 14n);
assertEq(a15, 15n);
assertEq(a16, 16n);
assertEq(a17, 17n);
assertEq(a18, 18n);
assertEq(a19, 19n);
assertEq(a20, 20n);
},
};
let instance: WebAssembly.Instance;
addImportsToImports(importObj, imports, name => instance.exports[name]);
const wasi = addWasiToImports(importObj);
const wasm = new Exports();
await wasm.instantiate(getWasm(), importObj);
wasi.start(wasm.instance);
instance = wasm.instance;
wasm.manyArguments(
1n,
2n,
3n,
4n,
5n,
6n,
7n,
8n,
9n,
10n,
11n,
12n,
13n,
14n,
15n,
16n,
17n,
18n,
19n,
20n,
);
}
await run()

View File

@@ -0,0 +1,22 @@
many-arguments: func(
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
a8: u64,
a9: u64,
a10: u64,
a11: u64,
a12: u64,
a13: u64,
a14: u64,
a15: u64,
a16: u64,
a17: u64,
a18: u64,
a19: u64,
a20: u64,
)

View File

@@ -0,0 +1,72 @@
#include <assert.h>
#include <exports.h>
#include <imports.h>
#include <limits.h>
#include <math.h>
void exports_many_arguments(
uint64_t a1,
uint64_t a2,
uint64_t a3,
uint64_t a4,
uint64_t a5,
uint64_t a6,
uint64_t a7,
uint64_t a8,
uint64_t a9,
uint64_t a10,
uint64_t a11,
uint64_t a12,
uint64_t a13,
uint64_t a14,
uint64_t a15,
uint64_t a16,
uint64_t a17,
uint64_t a18,
uint64_t a19,
uint64_t a20
) {
assert(a1 == 1);
assert(a2 == 2);
assert(a3 == 3);
assert(a4 == 4);
assert(a5 == 5);
assert(a6 == 6);
assert(a7 == 7);
assert(a8 == 8);
assert(a9 == 9);
assert(a10 == 10);
assert(a11 == 11);
assert(a12 == 12);
assert(a13 == 13);
assert(a14 == 14);
assert(a15 == 15);
assert(a16 == 16);
assert(a17 == 17);
assert(a18 == 18);
assert(a19 == 19);
assert(a20 == 20);
imports_many_arguments(
a1,
a2,
a3,
a4,
a5,
a6,
a7,
a8,
a9,
a10,
a11,
a12,
a13,
a14,
a15,
a16,
a17,
a18,
a19,
a20
);
}

View File

@@ -0,0 +1,56 @@
wit_bindgen_rust::import!("../../tests/runtime/many_arguments/imports.wit");
wit_bindgen_rust::export!("../../tests/runtime/many_arguments/exports.wit");
use imports::*;
struct Exports;
impl exports::Exports for Exports {
fn many_arguments(
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
a8: u64,
a9: u64,
a10: u64,
a11: u64,
a12: u64,
a13: u64,
a14: u64,
a15: u64,
a16: u64,
a17: u64,
a18: u64,
a19: u64,
a20: u64,
) {
assert_eq!(a1, 1);
assert_eq!(a2, 2);
assert_eq!(a3, 3);
assert_eq!(a4, 4);
assert_eq!(a5, 5);
assert_eq!(a6, 6);
assert_eq!(a7, 7);
assert_eq!(a8, 8);
assert_eq!(a9, 9);
assert_eq!(a10, 10);
assert_eq!(a11, 11);
assert_eq!(a12, 12);
assert_eq!(a13, 13);
assert_eq!(a14, 14);
assert_eq!(a15, 15);
assert_eq!(a16, 16);
assert_eq!(a17, 17);
assert_eq!(a18, 18);
assert_eq!(a19, 19);
assert_eq!(a20, 20);
many_arguments(
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19,
a20,
);
}
}

View File

@@ -0,0 +1,16 @@
test-imports: func()
roundtrip-u8: func(a: u8) -> u8
roundtrip-s8: func(a: s8) -> s8
roundtrip-u16: func(a: u16) -> u16
roundtrip-s16: func(a: s16) -> s16
roundtrip-u32: func(a: u32) -> u32
roundtrip-s32: func(a: s32) -> s32
roundtrip-u64: func(a: u64) -> u64
roundtrip-s64: func(a: s64) -> s64
roundtrip-float32: func(a: float32) -> float32
roundtrip-float64: func(a: float64) -> float64
roundtrip-char: func(a: char) -> char
set-scalar: func(a: u32)
get-scalar: func() -> u32

View File

@@ -0,0 +1,106 @@
from exports.bindings import Exports
from imports.bindings import add_imports_to_linker, Imports
import math;
import sys
import wasmtime
class MyImports:
def roundtrip_u8(self, a: int) -> int:
return a
def roundtrip_s8(self, a: int) -> int:
return a
def roundtrip_u16(self, a: int) -> int:
return a
def roundtrip_s16(self, a: int) -> int:
return a
def roundtrip_u32(self, a: int) -> int:
return a
def roundtrip_s32(self, a: int) -> int:
return a
def roundtrip_u64(self, a: int) -> int:
return a
def roundtrip_s64(self, a: int) -> int:
return a
def roundtrip_float32(self, a: float) -> float:
return a
def roundtrip_float64(self, a: float) -> float:
return a
def roundtrip_char(self, a: str) -> str:
return a
def set_scalar(self, a: int) -> None:
self.scalar = a
def get_scalar(self) -> int:
return self.scalar
def run(wasm_file: str) -> None:
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, wasm_file)
linker = wasmtime.Linker(store.engine)
linker.define_wasi()
wasi = wasmtime.WasiConfig()
wasi.inherit_stdout()
wasi.inherit_stderr()
store.set_wasi(wasi)
imports = MyImports()
add_imports_to_linker(linker, store, imports)
wasm = Exports(store, linker, module)
wasm.test_imports(store)
assert(wasm.roundtrip_u8(store, 1) == 1)
assert(wasm.roundtrip_u8(store, (1 << 8) - 1) == (1 << 8) - 1)
assert(wasm.roundtrip_u16(store, 1) == 1)
assert(wasm.roundtrip_u16(store, (1 << 16) - 1) == (1 << 16) - 1)
assert(wasm.roundtrip_u32(store, 1) == 1)
assert(wasm.roundtrip_u32(store, (1 << 32) - 1) == (1 << 32) - 1)
assert(wasm.roundtrip_u64(store, 1) == 1)
assert(wasm.roundtrip_u64(store, (1 << 64) - 1) == (1 << 64) - 1)
assert(wasm.roundtrip_s8(store, 1) == 1)
assert(wasm.roundtrip_s8(store, (1 << (8 - 1) - 1)) == (1 << (8 - 1) - 1))
assert(wasm.roundtrip_s8(store, -(1 << (8 - 1))) == -(1 << (8 - 1)))
assert(wasm.roundtrip_s16(store, 1) == 1)
assert(wasm.roundtrip_s16(store, (1 << (16 - 1) - 1)) == (1 << (16 - 1) - 1))
assert(wasm.roundtrip_s16(store, -(1 << (16 - 1))) == -(1 << (16 - 1)))
assert(wasm.roundtrip_s32(store, 1) == 1)
assert(wasm.roundtrip_s32(store, (1 << (32 - 1) - 1)) == (1 << (32 - 1) - 1))
assert(wasm.roundtrip_s32(store, -(1 << (32 - 1))) == -(1 << (32 - 1)))
assert(wasm.roundtrip_s64(store, 1) == 1)
assert(wasm.roundtrip_s64(store, (1 << (64 - 1) - 1)) == (1 << (64 - 1) - 1))
assert(wasm.roundtrip_s64(store, -(1 << (64 - 1))) == -(1 << (64 - 1)))
inf = float('inf')
assert(wasm.roundtrip_float32(store, 1.0) == 1.0)
assert(wasm.roundtrip_float32(store, inf) == inf)
assert(wasm.roundtrip_float32(store, -inf) == -inf)
assert(math.isnan(wasm.roundtrip_float32(store, float('nan'))))
assert(wasm.roundtrip_float64(store, 1.0) == 1.0)
assert(wasm.roundtrip_float64(store, inf) == inf)
assert(wasm.roundtrip_float64(store, -inf) == -inf)
assert(math.isnan(wasm.roundtrip_float64(store, float('nan'))))
assert(wasm.roundtrip_char(store, 'a') == 'a')
assert(wasm.roundtrip_char(store, ' ') == ' ')
assert(wasm.roundtrip_char(store, '🚩') == '🚩')
wasm.set_scalar(store, 2)
assert(wasm.get_scalar(store) == 2)
wasm.set_scalar(store, 4)
assert(wasm.get_scalar(store) == 4)
if __name__ == '__main__':
run(sys.argv[1])

View File

@@ -0,0 +1,188 @@
use anyhow::Result;
wit_bindgen_wasmtime::export!("../../tests/runtime/numbers/imports.wit");
#[derive(Default)]
pub struct MyImports {
scalar: u32,
}
impl imports::Imports for MyImports {
fn roundtrip_u8(&mut self, val: u8) -> u8 {
val
}
fn roundtrip_s8(&mut self, val: i8) -> i8 {
val
}
fn roundtrip_u16(&mut self, val: u16) -> u16 {
val
}
fn roundtrip_s16(&mut self, val: i16) -> i16 {
val
}
fn roundtrip_u32(&mut self, val: u32) -> u32 {
val
}
fn roundtrip_s32(&mut self, val: i32) -> i32 {
val
}
fn roundtrip_u64(&mut self, val: u64) -> u64 {
val
}
fn roundtrip_s64(&mut self, val: i64) -> i64 {
val
}
fn roundtrip_float32(&mut self, val: f32) -> f32 {
val
}
fn roundtrip_float64(&mut self, val: f64) -> f64 {
val
}
fn roundtrip_char(&mut self, val: char) -> char {
val
}
fn set_scalar(&mut self, val: u32) {
self.scalar = val;
}
fn get_scalar(&mut self) -> u32 {
self.scalar
}
}
wit_bindgen_wasmtime::import!("../../tests/runtime/numbers/exports.wit");
fn run(wasm: &str) -> Result<()> {
let (exports, mut store) = crate::instantiate(
wasm,
|linker| imports::add_to_linker(linker, |cx| -> &mut MyImports { &mut cx.imports }),
|store, module, linker| {
exports::Exports::instantiate(store, module, linker, |cx| &mut cx.exports)
},
)?;
exports.test_imports(&mut store)?;
assert_eq!(exports.roundtrip_u8(&mut store, 1)?, 1);
assert_eq!(
exports.roundtrip_u8(&mut store, u8::min_value())?,
u8::min_value()
);
assert_eq!(
exports.roundtrip_u8(&mut store, u8::max_value())?,
u8::max_value()
);
assert_eq!(exports.roundtrip_s8(&mut store, 1)?, 1);
assert_eq!(
exports.roundtrip_s8(&mut store, i8::min_value())?,
i8::min_value()
);
assert_eq!(
exports.roundtrip_s8(&mut store, i8::max_value())?,
i8::max_value()
);
assert_eq!(exports.roundtrip_u16(&mut store, 1)?, 1);
assert_eq!(
exports.roundtrip_u16(&mut store, u16::min_value())?,
u16::min_value()
);
assert_eq!(
exports.roundtrip_u16(&mut store, u16::max_value())?,
u16::max_value()
);
assert_eq!(exports.roundtrip_s16(&mut store, 1)?, 1);
assert_eq!(
exports.roundtrip_s16(&mut store, i16::min_value())?,
i16::min_value()
);
assert_eq!(
exports.roundtrip_s16(&mut store, i16::max_value())?,
i16::max_value()
);
assert_eq!(exports.roundtrip_u32(&mut store, 1)?, 1);
assert_eq!(
exports.roundtrip_u32(&mut store, u32::min_value())?,
u32::min_value()
);
assert_eq!(
exports.roundtrip_u32(&mut store, u32::max_value())?,
u32::max_value()
);
assert_eq!(exports.roundtrip_s32(&mut store, 1)?, 1);
assert_eq!(
exports.roundtrip_s32(&mut store, i32::min_value())?,
i32::min_value()
);
assert_eq!(
exports.roundtrip_s32(&mut store, i32::max_value())?,
i32::max_value()
);
assert_eq!(exports.roundtrip_u64(&mut store, 1)?, 1);
assert_eq!(
exports.roundtrip_u64(&mut store, u64::min_value())?,
u64::min_value()
);
assert_eq!(
exports.roundtrip_u64(&mut store, u64::max_value())?,
u64::max_value()
);
assert_eq!(exports.roundtrip_s64(&mut store, 1)?, 1);
assert_eq!(
exports.roundtrip_s64(&mut store, i64::min_value())?,
i64::min_value()
);
assert_eq!(
exports.roundtrip_s64(&mut store, i64::max_value())?,
i64::max_value()
);
assert_eq!(exports.roundtrip_float32(&mut store, 1.0)?, 1.0);
assert_eq!(
exports.roundtrip_float32(&mut store, f32::INFINITY)?,
f32::INFINITY
);
assert_eq!(
exports.roundtrip_float32(&mut store, f32::NEG_INFINITY)?,
f32::NEG_INFINITY
);
assert!(exports.roundtrip_float32(&mut store, f32::NAN)?.is_nan());
assert_eq!(exports.roundtrip_float64(&mut store, 1.0)?, 1.0);
assert_eq!(
exports.roundtrip_float64(&mut store, f64::INFINITY)?,
f64::INFINITY
);
assert_eq!(
exports.roundtrip_float64(&mut store, f64::NEG_INFINITY)?,
f64::NEG_INFINITY
);
assert!(exports.roundtrip_float64(&mut store, f64::NAN)?.is_nan());
assert_eq!(exports.roundtrip_char(&mut store, 'a')?, 'a');
assert_eq!(exports.roundtrip_char(&mut store, ' ')?, ' ');
assert_eq!(exports.roundtrip_char(&mut store, '🚩')?, '🚩');
exports.set_scalar(&mut store, 2)?;
assert_eq!(exports.get_scalar(&mut store)?, 2);
exports.set_scalar(&mut store, 4)?;
assert_eq!(exports.get_scalar(&mut store)?, 4);
Ok(())
}

View File

@@ -0,0 +1,89 @@
import { addImportsToImports, Imports } from "./imports.js";
import { Exports } from "./exports.js";
import { getWasm, addWasiToImports } from "./helpers.js";
function assertEq(x: any, y: any) {
if (x !== y)
throw new Error(`${x} != ${y}`);
}
function assert(x: boolean) {
if (!x)
throw new Error("assert failed");
}
async function run() {
const importObj = {};
let scalar = 0;
addImportsToImports(importObj, {
roundtripU8(x) { return x; },
roundtripS8(x) { return x; },
roundtripU16(x) { return x; },
roundtripS16(x) { return x; },
roundtripU32(x) { return x; },
roundtripS32(x) { return x; },
roundtripU64(x) { return x; },
roundtripS64(x) { return x; },
roundtripFloat32(x) { return x; },
roundtripFloat64(x) { return x; },
roundtripChar(x) { return x; },
setScalar(x) { scalar = x; },
getScalar() { return scalar; },
});
const wasi = addWasiToImports(importObj);
const wasm = new Exports();
await wasm.instantiate(getWasm(), importObj);
wasi.start(wasm.instance);
wasm.testImports();
assertEq(wasm.roundtripU8(1), 1);
assertEq(wasm.roundtripU8((1 << 8) - 1), (1 << 8) - 1);
assertEq(wasm.roundtripS8(1), 1);
assertEq(wasm.roundtripS8((1 << 7) - 1), (1 << 7) - 1);
assertEq(wasm.roundtripS8(-(1 << 7)), -(1 << 7));
assertEq(wasm.roundtripU16(1), 1);
assertEq(wasm.roundtripU16((1 << 16) - 1), (1 << 16) - 1);
assertEq(wasm.roundtripS16(1), 1);
assertEq(wasm.roundtripS16((1 << 15) - 1), (1 << 15) - 1);
assertEq(wasm.roundtripS16(-(1 << 15)), -(1 << 15));
assertEq(wasm.roundtripU32(1), 1);
assertEq(wasm.roundtripU32(~0 >>> 0), ~0 >>> 0);
assertEq(wasm.roundtripS32(1), 1);
assertEq(wasm.roundtripS32(((1 << 31) - 1) >>> 0), ((1 << 31) - 1) >>> 0);
assertEq(wasm.roundtripS32(1 << 31), 1 << 31);
assertEq(wasm.roundtripU64(1n), 1n);
assertEq(wasm.roundtripU64((1n << 64n) - 1n), (1n << 64n) - 1n);
assertEq(wasm.roundtripS64(1n), 1n);
assertEq(wasm.roundtripS64((1n << 63n) - 1n), (1n << 63n) - 1n);
assertEq(wasm.roundtripS64(-(1n << 63n)), -(1n << 63n));
assertEq(wasm.roundtripFloat32(1), 1);
assertEq(wasm.roundtripFloat32(Infinity), Infinity);
assertEq(wasm.roundtripFloat32(-Infinity), -Infinity);
assert(Number.isNaN(wasm.roundtripFloat32(NaN)));
assertEq(wasm.roundtripFloat64(1), 1);
assertEq(wasm.roundtripFloat64(Infinity), Infinity);
assertEq(wasm.roundtripFloat64(-Infinity), -Infinity);
assert(Number.isNaN(wasm.roundtripFloat64(NaN)));
assertEq(wasm.roundtripChar('a'), 'a');
assertEq(wasm.roundtripChar(' '), ' ');
assertEq(wasm.roundtripChar('🚩'), '🚩');
wasm.setScalar(2);
assertEq(wasm.getScalar(), 2);
wasm.setScalar(4);
assertEq(wasm.getScalar(), 4);
}
await run()

View File

@@ -0,0 +1,14 @@
roundtrip-u8: func(a: u8) -> u8
roundtrip-s8: func(a: s8) -> s8
roundtrip-u16: func(a: u16) -> u16
roundtrip-s16: func(a: s16) -> s16
roundtrip-u32: func(a: u32) -> u32
roundtrip-s32: func(a: s32) -> s32
roundtrip-u64: func(a: u64) -> u64
roundtrip-s64: func(a: s64) -> s64
roundtrip-float32: func(a: float32) -> float32
roundtrip-float64: func(a: float64) -> float64
roundtrip-char: func(a: char) -> char
set-scalar: func(a: u32)
get-scalar: func() -> u32

View File

@@ -0,0 +1,113 @@
#include <assert.h>
#include <exports.h>
#include <imports.h>
#include <limits.h>
#include <math.h>
uint8_t exports_roundtrip_u8(uint8_t a) {
return a;
}
int8_t exports_roundtrip_s8(int8_t a) {
return a;
}
uint16_t exports_roundtrip_u16(uint16_t a) {
return a;
}
int16_t exports_roundtrip_s16(int16_t a) {
return a;
}
uint32_t exports_roundtrip_u32(uint32_t a) {
return a;
}
int32_t exports_roundtrip_s32(int32_t a) {
return a;
}
uint64_t exports_roundtrip_u64(uint64_t a) {
return a;
}
int64_t exports_roundtrip_s64(int64_t a) {
return a;
}
float exports_roundtrip_float32(float a) {
return a;
}
double exports_roundtrip_float64(double a) {
return a;
}
uint32_t exports_roundtrip_char(uint32_t a) {
return a;
}
static uint32_t SCALAR = 0;
void exports_set_scalar(uint32_t a) {
SCALAR = a;
}
uint32_t exports_get_scalar(void) {
return SCALAR;
}
void exports_test_imports() {
assert(imports_roundtrip_u8(1) == 1);
assert(imports_roundtrip_u8(0) == 0);
assert(imports_roundtrip_u8(UCHAR_MAX) == UCHAR_MAX);
assert(imports_roundtrip_s8(1) == 1);
assert(imports_roundtrip_s8(SCHAR_MIN) == SCHAR_MIN);
assert(imports_roundtrip_s8(SCHAR_MAX) == SCHAR_MAX);
assert(imports_roundtrip_u16(1) == 1);
assert(imports_roundtrip_u16(0) == 0);
assert(imports_roundtrip_u16(USHRT_MAX) == USHRT_MAX);
assert(imports_roundtrip_s16(1) == 1);
assert(imports_roundtrip_s16(SHRT_MIN) == SHRT_MIN);
assert(imports_roundtrip_s16(SHRT_MAX) == SHRT_MAX);
assert(imports_roundtrip_u32(1) == 1);
assert(imports_roundtrip_u32(0) == 0);
assert(imports_roundtrip_u32(UINT_MAX) == UINT_MAX);
assert(imports_roundtrip_s32(1) == 1);
assert(imports_roundtrip_s32(INT_MIN) == INT_MIN);
assert(imports_roundtrip_s32(INT_MAX) == INT_MAX);
assert(imports_roundtrip_u64(1) == 1);
assert(imports_roundtrip_u64(0) == 0);
assert(imports_roundtrip_u64(ULONG_MAX) == ULONG_MAX);
assert(imports_roundtrip_s64(1) == 1);
assert(imports_roundtrip_s64(LONG_MIN) == LONG_MIN);
assert(imports_roundtrip_s64(LONG_MAX) == LONG_MAX);
assert(imports_roundtrip_float32(1.0) == 1.0);
assert(imports_roundtrip_float32(INFINITY) == INFINITY);
assert(imports_roundtrip_float32(-INFINITY) == -INFINITY);
assert(isnan(imports_roundtrip_float32(NAN)));
assert(imports_roundtrip_float64(1.0) == 1.0);
assert(imports_roundtrip_float64(INFINITY) == INFINITY);
assert(imports_roundtrip_float64(-INFINITY) == -INFINITY);
assert(isnan(imports_roundtrip_float64(NAN)));
assert(imports_roundtrip_char('a') == 'a');
assert(imports_roundtrip_char(' ') == ' ');
assert(imports_roundtrip_char(U'🚩') == U'🚩');
imports_set_scalar(2);
assert(imports_get_scalar() == 2);
imports_set_scalar(4);
assert(imports_get_scalar() == 4);
}

View File

@@ -0,0 +1,116 @@
wit_bindgen_rust::import!("../../tests/runtime/numbers/imports.wit");
wit_bindgen_rust::export!("../../tests/runtime/numbers/exports.wit");
use imports::*;
use std::sync::atomic::{AtomicU32, Ordering::SeqCst};
struct Exports;
static SCALAR: AtomicU32 = AtomicU32::new(0);
impl exports::Exports for Exports {
fn test_imports() {
assert_eq!(roundtrip_u8(1), 1);
assert_eq!(roundtrip_u8(u8::min_value()), u8::min_value());
assert_eq!(roundtrip_u8(u8::max_value()), u8::max_value());
assert_eq!(roundtrip_s8(1), 1);
assert_eq!(roundtrip_s8(i8::min_value()), i8::min_value());
assert_eq!(roundtrip_s8(i8::max_value()), i8::max_value());
assert_eq!(roundtrip_u16(1), 1);
assert_eq!(roundtrip_u16(u16::min_value()), u16::min_value());
assert_eq!(roundtrip_u16(u16::max_value()), u16::max_value());
assert_eq!(roundtrip_s16(1), 1);
assert_eq!(roundtrip_s16(i16::min_value()), i16::min_value());
assert_eq!(roundtrip_s16(i16::max_value()), i16::max_value());
assert_eq!(roundtrip_u32(1), 1);
assert_eq!(roundtrip_u32(u32::min_value()), u32::min_value());
assert_eq!(roundtrip_u32(u32::max_value()), u32::max_value());
assert_eq!(roundtrip_s32(1), 1);
assert_eq!(roundtrip_s32(i32::min_value()), i32::min_value());
assert_eq!(roundtrip_s32(i32::max_value()), i32::max_value());
assert_eq!(roundtrip_u64(1), 1);
assert_eq!(roundtrip_u64(u64::min_value()), u64::min_value());
assert_eq!(roundtrip_u64(u64::max_value()), u64::max_value());
assert_eq!(roundtrip_s64(1), 1);
assert_eq!(roundtrip_s64(i64::min_value()), i64::min_value());
assert_eq!(roundtrip_s64(i64::max_value()), i64::max_value());
assert_eq!(roundtrip_float32(1.0), 1.0);
assert_eq!(roundtrip_float32(f32::INFINITY), f32::INFINITY);
assert_eq!(roundtrip_float32(f32::NEG_INFINITY), f32::NEG_INFINITY);
assert!(roundtrip_float32(f32::NAN).is_nan());
assert_eq!(roundtrip_float64(1.0), 1.0);
assert_eq!(roundtrip_float64(f64::INFINITY), f64::INFINITY);
assert_eq!(roundtrip_float64(f64::NEG_INFINITY), f64::NEG_INFINITY);
assert!(roundtrip_float64(f64::NAN).is_nan());
assert_eq!(roundtrip_char('a'), 'a');
assert_eq!(roundtrip_char(' '), ' ');
assert_eq!(roundtrip_char('🚩'), '🚩');
set_scalar(2);
assert_eq!(get_scalar(), 2);
set_scalar(4);
assert_eq!(get_scalar(), 4);
}
fn roundtrip_u8(a: u8) -> u8 {
a
}
fn roundtrip_s8(a: i8) -> i8 {
a
}
fn roundtrip_u16(a: u16) -> u16 {
a
}
fn roundtrip_s16(a: i16) -> i16 {
a
}
fn roundtrip_u32(a: u32) -> u32 {
a
}
fn roundtrip_s32(a: i32) -> i32 {
a
}
fn roundtrip_u64(a: u64) -> u64 {
a
}
fn roundtrip_s64(a: i64) -> i64 {
a
}
fn roundtrip_float32(a: f32) -> f32 {
a
}
fn roundtrip_float64(a: f64) -> f64 {
a
}
fn roundtrip_char(a: char) -> char {
a
}
fn set_scalar(val: u32) {
SCALAR.store(val, SeqCst)
}
fn get_scalar() -> u32 {
SCALAR.load(SeqCst)
}
}

View File

@@ -0,0 +1,46 @@
test-imports: func()
multiple-results: func() -> tuple<u8, u16>
swap-tuple: func(a: tuple<u8, u32>) -> tuple<u32, u8>
flags f1 { a, b }
roundtrip-flags1: func(a: f1) -> f1
flags f2 { c, d, e }
roundtrip-flags2: func(a: f2) -> f2
flags f8 {
b0, b1, b2, b3, b4, b5, b6, b7,
}
flags f16 {
b0, b1, b2, b3, b4, b5, b6, b7,
b8, b9, b10, b11, b12, b13, b14, b15,
}
flags f32 {
b0, b1, b2, b3, b4, b5, b6, b7,
b8, b9, b10, b11, b12, b13, b14, b15,
b16, b17, b18, b19, b20, b21, b22, b23,
b24, b25, b26, b27, b28, b29, b30, b31,
}
flags f64 {
b0, b1, b2, b3, b4, b5, b6, b7,
b8, b9, b10, b11, b12, b13, b14, b15,
b16, b17, b18, b19, b20, b21, b22, b23,
b24, b25, b26, b27, b28, b29, b30, b31,
b32, b33, b34, b35, b36, b37, b38, b39,
b40, b41, b42, b43, b44, b45, b46, b47,
b48, b49, b50, b51, b52, b53, b54, b55,
b56, b57, b58, b59, b60, b61, b62, b63,
}
roundtrip-flags3: func(a: f8, b: f16, c: f32, d: f64) -> tuple<f8, f16, f32, f64>
record r1 { a: u8, b: f1 }
roundtrip-record1: func(a: r1) -> r1
tuple0: func(a: tuple<>) -> tuple<>
tuple1: func(a: tuple<u8>) -> tuple<u8>

View File

@@ -0,0 +1,72 @@
from exports.bindings import Exports
from imports.bindings import add_imports_to_linker, Imports
from typing import Tuple
import exports.bindings as e
import imports.bindings as i
import sys
import wasmtime
class MyImports:
def multiple_results(self) -> Tuple[int, int]:
return (4, 5)
def swap_tuple(self, a: Tuple[int, int]) -> Tuple[int, int]:
return (a[1], a[0])
def roundtrip_flags1(self, a: i.F1) -> i.F1:
return a
def roundtrip_flags2(self, a: i.F2) -> i.F2:
return a
def roundtrip_flags3(self, a: i.Flag8, b: i.Flag16, c: i.Flag32, d: i.Flag64) -> Tuple[i.Flag8, i.Flag16, i.Flag32, i.Flag64]:
return (a, b, c, d)
def roundtrip_record1(self, a: i.R1) -> i.R1:
return a
def tuple0(self, a: None) -> None:
pass
def tuple1(self, a: Tuple[int]) -> Tuple[int]:
return (a[0],)
def run(wasm_file: str) -> None:
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, wasm_file)
linker = wasmtime.Linker(store.engine)
linker.define_wasi()
wasi = wasmtime.WasiConfig()
wasi.inherit_stdout()
wasi.inherit_stderr()
store.set_wasi(wasi)
imports = MyImports()
add_imports_to_linker(linker, store, imports)
wasm = Exports(store, linker, module)
wasm.test_imports(store)
assert(wasm.multiple_results(store) == (100, 200))
assert(wasm.swap_tuple(store, (1, 2)) == (2, 1))
assert(wasm.roundtrip_flags1(store, e.F1.A) == e.F1.A)
assert(wasm.roundtrip_flags1(store, e.F1(0)) == e.F1(0))
assert(wasm.roundtrip_flags1(store, e.F1.A | e.F1.B) == (e.F1.A | e.F1.B))
assert(wasm.roundtrip_flags2(store, e.F2.C) == e.F2.C)
assert(wasm.roundtrip_flags2(store, e.F2(0)) == e.F2(0))
assert(wasm.roundtrip_flags2(store, e.F2.D) == e.F2.D)
assert(wasm.roundtrip_flags2(store, e.F2.C | e.F2.E) == (e.F2.C | e.F2.E))
r = wasm.roundtrip_record1(store, e.R1(8, e.F1(0)))
assert(r.a == 8)
assert(r.b == e.F1(0))
r = wasm.roundtrip_record1(store, e.R1(a=0, b=e.F1.A | e.F1.B))
assert(r.a == 0)
assert(r.b == (e.F1.A | e.F1.B))
wasm.tuple0(store, None)
assert(wasm.tuple1(store, (1,)) == (1,))
if __name__ == '__main__':
run(sys.argv[1])

View File

@@ -0,0 +1,111 @@
use anyhow::Result;
wit_bindgen_wasmtime::export!("../../tests/runtime/records/imports.wit");
use imports::*;
#[derive(Default)]
pub struct MyImports;
impl Imports for MyImports {
fn multiple_results(&mut self) -> (u8, u16) {
(4, 5)
}
fn swap_tuple(&mut self, a: (u8, u32)) -> (u32, u8) {
(a.1, a.0)
}
fn roundtrip_flags1(&mut self, a: F1) -> F1 {
drop(a.to_string());
drop(format!("{:?}", a));
drop(a & F1::all());
a
}
fn roundtrip_flags2(&mut self, a: F2) -> F2 {
a
}
fn roundtrip_flags3(
&mut self,
a: Flag8,
b: Flag16,
c: Flag32,
d: Flag64,
) -> (Flag8, Flag16, Flag32, Flag64) {
(a, b, c, d)
}
fn roundtrip_record1(&mut self, a: R1) -> R1 {
drop(format!("{:?}", a));
a
}
fn tuple0(&mut self, _: ()) {}
fn tuple1(&mut self, a: (u8,)) -> (u8,) {
(a.0,)
}
}
wit_bindgen_wasmtime::import!("../../tests/runtime/records/exports.wit");
fn run(wasm: &str) -> Result<()> {
use exports::*;
let (exports, mut store) = crate::instantiate(
wasm,
|linker| imports::add_to_linker(linker, |cx| -> &mut MyImports { &mut cx.imports }),
|store, module, linker| Exports::instantiate(store, module, linker, |cx| &mut cx.exports),
)?;
exports.test_imports(&mut store)?;
assert_eq!(exports.multiple_results(&mut store,)?, (100, 200));
assert_eq!(exports.swap_tuple(&mut store, (1u8, 2u32))?, (2u32, 1u8));
assert_eq!(exports.roundtrip_flags1(&mut store, F1::A)?, F1::A);
assert_eq!(
exports.roundtrip_flags1(&mut store, F1::empty())?,
F1::empty()
);
assert_eq!(exports.roundtrip_flags1(&mut store, F1::B)?, F1::B);
assert_eq!(
exports.roundtrip_flags1(&mut store, F1::A | F1::B)?,
F1::A | F1::B
);
assert_eq!(exports.roundtrip_flags2(&mut store, F2::C)?, F2::C);
assert_eq!(
exports.roundtrip_flags2(&mut store, F2::empty())?,
F2::empty()
);
assert_eq!(exports.roundtrip_flags2(&mut store, F2::D)?, F2::D);
assert_eq!(
exports.roundtrip_flags2(&mut store, F2::C | F2::E)?,
F2::C | F2::E
);
let r = exports.roundtrip_record1(
&mut store,
R1 {
a: 8,
b: F1::empty(),
},
)?;
assert_eq!(r.a, 8);
assert_eq!(r.b, F1::empty());
let r = exports.roundtrip_record1(
&mut store,
R1 {
a: 0,
b: F1::A | F1::B,
},
)?;
assert_eq!(r.a, 0);
assert_eq!(r.b, F1::A | F1::B);
assert_eq!(exports.tuple0(&mut store, ())?, ());
assert_eq!(exports.tuple1(&mut store, (1,))?, (1,));
Ok(())
}

View File

@@ -0,0 +1,57 @@
import { addImportsToImports, Imports } from "./imports.js";
import { Exports } from "./exports.js";
import * as exports from "./exports.js";
import { getWasm, addWasiToImports } from "./helpers.js";
// @ts-ignore
import * as assert from 'assert';
async function run() {
const importObj = {};
const imports: Imports = {
multipleResults() { return [4, 5]; },
swapTuple([a, b]) { return [b, a]; },
roundtripFlags1(x) { return x; },
roundtripFlags2(x) { return x; },
roundtripFlags3(r0, r1, r2, r3) { return [r0, r1, r2, r3]; },
roundtripRecord1(x) { return x; },
tuple0([]) { return []; },
tuple1([x]) { return [x]; },
};
let instance: WebAssembly.Instance;
addImportsToImports(importObj, imports, name => instance.exports[name]);
const wasi = addWasiToImports(importObj);
const wasm = new Exports();
await wasm.instantiate(getWasm(), importObj);
wasi.start(wasm.instance);
instance = wasm.instance;
wasm.testImports();
assert.deepEqual(wasm.multipleResults(), [100, 200]);
assert.deepStrictEqual(wasm.swapTuple([1, 2]), [2, 1]);
assert.deepEqual(wasm.roundtripFlags1(exports.F1_A), exports.F1_A);
assert.deepEqual(wasm.roundtripFlags1(0), 0);
assert.deepEqual(wasm.roundtripFlags1(exports.F1_A | exports.F1_B), exports.F1_A | exports.F1_B);
assert.deepEqual(wasm.roundtripFlags2(exports.F2_C), exports.F2_C);
assert.deepEqual(wasm.roundtripFlags2(0), 0);
assert.deepEqual(wasm.roundtripFlags2(exports.F2_D), exports.F2_D);
assert.deepEqual(wasm.roundtripFlags2(exports.F2_C | exports.F2_E), exports.F2_C | exports.F2_E);
{
const { a, b } = wasm.roundtripRecord1({ a: 8, b: 0 });
assert.deepEqual(a, 8);
assert.deepEqual(b, 0);
}
{
const { a, b } = wasm.roundtripRecord1({ a: 0, b: exports.F1_A | exports.F1_B });
assert.deepEqual(a, 0);
assert.deepEqual(b, exports.F1_A | exports.F1_B);
}
assert.deepStrictEqual(wasm.tuple0([]), []);
assert.deepStrictEqual(wasm.tuple1([1]), [1]);
}
await run()

View File

@@ -0,0 +1,44 @@
multiple-results: func() -> tuple<u8, u16>
swap-tuple: func(a: tuple<u8, u32>) -> tuple<u32, u8>
flags f1 { a, b }
roundtrip-flags1: func(a: f1) -> f1
flags f2 { c, d, e }
roundtrip-flags2: func(a: f2) -> f2
flags flag8 {
b0, b1, b2, b3, b4, b5, b6, b7,
}
flags flag16 {
b0, b1, b2, b3, b4, b5, b6, b7,
b8, b9, b10, b11, b12, b13, b14, b15,
}
flags flag32 {
b0, b1, b2, b3, b4, b5, b6, b7,
b8, b9, b10, b11, b12, b13, b14, b15,
b16, b17, b18, b19, b20, b21, b22, b23,
b24, b25, b26, b27, b28, b29, b30, b31,
}
flags flag64 {
b0, b1, b2, b3, b4, b5, b6, b7,
b8, b9, b10, b11, b12, b13, b14, b15,
b16, b17, b18, b19, b20, b21, b22, b23,
b24, b25, b26, b27, b28, b29, b30, b31,
b32, b33, b34, b35, b36, b37, b38, b39,
b40, b41, b42, b43, b44, b45, b46, b47,
b48, b49, b50, b51, b52, b53, b54, b55,
b56, b57, b58, b59, b60, b61, b62, b63,
}
roundtrip-flags3: func(a: flag8, b: flag16, c: flag32, d: flag64) -> tuple<flag8, flag16, flag32, flag64>
record r1 { a: u8, b: f1 }
roundtrip-record1: func(a: r1) -> r1
tuple0: func(a: tuple<>) -> tuple<>
tuple1: func(a: tuple<u8>) -> tuple<u8>

View File

@@ -0,0 +1,106 @@
#include <assert.h>
#include <imports.h>
#include <exports.h>
void exports_test_imports() {
{
uint8_t a;
uint16_t b;
imports_multiple_results(&a, &b);
assert(a == 4);
assert(b == 5);
}
imports_tuple2_u8_u32_t input;
input.f0 = 1;
input.f1 = 2;
uint32_t a;
uint8_t b;
imports_swap_tuple(&input, &a, &b);
assert(a == 2);
assert(b == 1);
assert(imports_roundtrip_flags1(IMPORTS_F1_A) == IMPORTS_F1_A);
assert(imports_roundtrip_flags1(0) == 0);
assert(imports_roundtrip_flags1(IMPORTS_F1_B) == IMPORTS_F1_B);
assert(imports_roundtrip_flags1(IMPORTS_F1_A | IMPORTS_F1_B) == (IMPORTS_F1_A | IMPORTS_F1_B));
assert(imports_roundtrip_flags2(IMPORTS_F2_C) == IMPORTS_F2_C);
assert(imports_roundtrip_flags2(0) == 0);
assert(imports_roundtrip_flags2(IMPORTS_F2_D) == IMPORTS_F2_D);
assert(imports_roundtrip_flags2(IMPORTS_F2_C | IMPORTS_F2_E) == (IMPORTS_F2_C | IMPORTS_F2_E));
imports_flag8_t flag8;
imports_flag16_t flag16;
imports_flag32_t flag32;
imports_flag64_t flag64;
imports_roundtrip_flags3(IMPORTS_FLAG8_B0, IMPORTS_FLAG16_B1, IMPORTS_FLAG32_B2, IMPORTS_FLAG64_B3,
&flag8, &flag16, &flag32, &flag64);
assert(flag8 == IMPORTS_FLAG8_B0);
assert(flag16 == IMPORTS_FLAG16_B1);
assert(flag32 == IMPORTS_FLAG32_B2);
assert(flag64 == IMPORTS_FLAG64_B3);
{
imports_r1_t a, b;
a.a = 8;
a.b = 0;
imports_roundtrip_record1(&a, &b);
assert(b.a == 8);
assert(b.b == 0);
}
{
imports_r1_t a, b;
a.a = 0;
a.b = IMPORTS_F1_A | IMPORTS_F1_B;
imports_roundtrip_record1(&a, &b);
assert(b.a == 0);
assert(b.b == (IMPORTS_F1_A | IMPORTS_F1_B));
}
imports_tuple0_t t0;
imports_tuple0(&t0);
imports_tuple1_u8_t t1;
t1.f0 = 1;
uint8_t ret;
imports_tuple1(&t1, &ret);
assert(ret == 1);
}
void exports_multiple_results(uint8_t *ret0, uint16_t *ret1) {
*ret0 = 100;
*ret1 = 200;
}
void exports_swap_tuple(exports_tuple2_u8_u32_t *a, uint32_t *ret0, uint8_t *ret1) {
*ret0 = a->f1;
*ret1 = a->f0;
}
exports_f1_t exports_roundtrip_flags1(exports_f1_t a) {
return a;
}
exports_f2_t exports_roundtrip_flags2(exports_f2_t a) {
return a;
}
void exports_roundtrip_flags3(exports_f8_t a, exports_f16_t b, exports_f32_t c, exports_f64_t d, exports_f8_t *ret0, exports_f16_t *ret1, exports_f32_t *ret2, exports_f64_t *ret3) {
*ret0 = a;
*ret1 = b;
*ret2 = c;
*ret3 = d;
}
void exports_roundtrip_record1(exports_r1_t *a, exports_r1_t *ret0) {
*ret0 = *a;
}
void exports_tuple0(exports_tuple0_t *a) {
}
void exports_tuple1(exports_tuple1_u8_t *a, uint8_t *ret0) {
*ret0 = a->f0;
}

View File

@@ -0,0 +1,77 @@
wit_bindgen_rust::import!("../../tests/runtime/records/imports.wit");
wit_bindgen_rust::export!("../../tests/runtime/records/exports.wit");
use exports::*;
struct Exports;
impl exports::Exports for Exports {
fn test_imports() {
use imports::*;
assert_eq!(multiple_results(), (4, 5));
assert_eq!(swap_tuple((1u8, 2u32)), (2u32, 1u8));
assert_eq!(roundtrip_flags1(F1::A), F1::A);
assert_eq!(roundtrip_flags1(F1::empty()), F1::empty());
assert_eq!(roundtrip_flags1(F1::B), F1::B);
assert_eq!(roundtrip_flags1(F1::A | F1::B), F1::A | F1::B);
assert_eq!(roundtrip_flags2(F2::C), F2::C);
assert_eq!(roundtrip_flags2(F2::empty()), F2::empty());
assert_eq!(roundtrip_flags2(F2::D), F2::D);
assert_eq!(roundtrip_flags2(F2::C | F2::E), F2::C | F2::E);
assert_eq!(
roundtrip_flags3(Flag8::B0, Flag16::B1, Flag32::B2, Flag64::B3),
(Flag8::B0, Flag16::B1, Flag32::B2, Flag64::B3)
);
let r = roundtrip_record1(R1 {
a: 8,
b: F1::empty(),
});
assert_eq!(r.a, 8);
assert_eq!(r.b, F1::empty());
let r = roundtrip_record1(R1 {
a: 0,
b: F1::A | F1::B,
});
assert_eq!(r.a, 0);
assert_eq!(r.b, F1::A | F1::B);
assert_eq!(tuple0(()), ());
assert_eq!(tuple1((1,)), (1,));
}
fn multiple_results() -> (u8, u16) {
(100, 200)
}
fn swap_tuple(a: (u8, u32)) -> (u32, u8) {
(a.1, a.0)
}
fn roundtrip_flags1(a: F1) -> F1 {
a
}
fn roundtrip_flags2(a: F2) -> F2 {
a
}
fn roundtrip_flags3(a: F8, b: F16, c: F32, d: F64) -> (F8, F16, F32, F64) {
(a, b, c, d)
}
fn roundtrip_record1(a: R1) -> R1 {
a
}
fn tuple0(_: ()) {}
fn tuple1(a: (u8,)) -> (u8,) {
(a.0,)
}
}

View File

@@ -0,0 +1 @@
thunk: func()

View File

@@ -0,0 +1,27 @@
from exports.bindings import Exports
from imports.bindings import add_imports_to_linker, Imports
import sys
import wasmtime
class MyImports:
def thunk(self) -> None:
self.hit = True
def run(wasm_file: str) -> None:
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, wasm_file)
linker = wasmtime.Linker(store.engine)
linker.define_wasi()
wasi = wasmtime.WasiConfig()
wasi.inherit_stdout()
wasi.inherit_stderr()
store.set_wasi(wasi)
imports = MyImports()
add_imports_to_linker(linker, store, imports)
wasm = Exports(store, linker, module)
wasm.thunk(store)
assert(imports.hit)
if __name__ == '__main__':
run(sys.argv[1])

View File

@@ -0,0 +1,33 @@
use anyhow::Result;
wit_bindgen_wasmtime::export!("../../tests/runtime/smoke/imports.wit");
#[derive(Default)]
pub struct MyImports {
hit: bool,
}
impl imports::Imports for MyImports {
fn thunk(&mut self) {
self.hit = true;
println!("in the host");
}
}
wit_bindgen_wasmtime::import!("../../tests/runtime/smoke/exports.wit");
fn run(wasm: &str) -> Result<()> {
let (exports, mut store) = crate::instantiate(
wasm,
|linker| imports::add_to_linker(linker, |cx| -> &mut MyImports { &mut cx.imports }),
|store, module, linker| {
exports::Exports::instantiate(store, module, linker, |cx| &mut cx.exports)
},
)?;
exports.thunk(&mut store)?;
assert!(store.data().imports.hit);
Ok(())
}

View File

@@ -0,0 +1,28 @@
import { addImportsToImports, Imports } from "./imports.js";
import { Exports } from "./exports.js";
import { getWasm, addWasiToImports } from "./helpers.js";
function assert(x: boolean, msg: string) {
if (!x)
throw new Error(msg);
}
async function run() {
const importObj = {};
let hit = false;
addImportsToImports(importObj, {
thunk() {
hit = true;
}
});
const wasi = addWasiToImports(importObj);
const wasm = new Exports();
await wasm.instantiate(getWasm(), importObj);
wasi.start(wasm.instance);
wasm.thunk();
assert(hit, "import not called");
}
await run()

View File

@@ -0,0 +1 @@
thunk: func()

View File

@@ -0,0 +1,6 @@
#include <imports.h>
#include <exports.h>
void exports_thunk() {
imports_thunk();
}

View File

@@ -0,0 +1,10 @@
wit_bindgen_rust::import!("../../tests/runtime/smoke/imports.wit");
wit_bindgen_rust::export!("../../tests/runtime/smoke/exports.wit");
struct Exports;
impl exports::Exports for Exports {
fn thunk() {
imports::thunk();
}
}

View File

@@ -0,0 +1,12 @@
test-imports: func()
f1: func()
f2: func(a: u32)
f3: func(a: u32, b: u32)
f4: func() -> u32
// TODO: shoudl re-enable when re-implemented
//f5: func() -> tuple<u32, u32>
//
//f6: func(a: u32, b: u32, c: u32) -> tuple<u32, u32, u32>

View File

@@ -0,0 +1,127 @@
use anyhow::Context;
wit_bindgen_wasmtime::export!("../../tests/runtime/smw_functions/imports.wit");
#[derive(Default)]
pub struct Host {
pub f1_called: bool,
pub f2_arg: u32,
pub f3_a: u32,
pub f3_b: u32,
pub f4_called: bool,
// pub f5_called: bool,
// pub f6_a: u32,
// pub f6_b: u32,
// pub f6_c: u32,
}
impl imports::Imports for Host {
fn f1(&mut self) {
self.f1_called = true;
}
fn f2(&mut self, arg: u32) {
self.f2_arg = arg;
}
fn f3(&mut self, a: u32, b: u32) {
self.f3_a = a;
self.f3_b = b;
}
fn f4(&mut self) -> u32 {
self.f4_called = true;
1337
}
// fn f5(&mut self) -> (u32, u32) {
// self.f5_called = true;
// (1, 2)
// }
// fn f6(&mut self, a: u32, b: u32, c: u32) -> (u32, u32, u32) {
// self.f6_a = a;
// self.f6_b = b;
// self.f6_c = c;
// (a + 1, b + 1, c + 1)
// }
}
wit_bindgen_wasmtime::import!("../../tests/runtime/smw_functions/exports.wit");
fn run(wasm: &str) -> anyhow::Result<()> {
let (exports, mut store) = crate::instantiate_smw(
wasm,
|linker| imports::add_to_linker(linker, |cx| -> &mut Host { &mut cx.imports }),
|store, module, linker| {
exports::Exports::instantiate(store, module, linker, |cx| &mut cx.exports)
},
)?;
// Test that the import instance called the functions we made available with
// the expected arguments.
exports.test_imports(&mut store)?;
assert!(
store.data().imports.f1_called,
"top-level JS imported and called `f1`",
);
assert_eq!(
store.data().imports.f2_arg,
42,
"f2 should have been called with 42",
);
assert_eq!(store.data().imports.f3_a, 0);
assert_eq!(store.data().imports.f3_b, u32::MAX);
assert!(
store.data().imports.f4_called,
"the top-level JS imported and called `f4`",
);
// assert!(
// store.data().imports.f5_called,
// "the top-level JS imported and called `f5`"
// );
// assert_eq!(store.data().imports.f6_a, 100);
// assert_eq!(store.data().imports.f6_b, 200);
// assert_eq!(store.data().imports.f6_c, 300);
// Test that the export instance behaves as we expect it to.
exports
.f1(&mut store)
.context("calling the `f1` export should succeed")?;
exports
.f2(&mut store, 42)
.context("calling the `f2` export should succeed")?;
exports
.f3(&mut store, 0, u32::MAX)
.context("calling the `f3` export should succeed")?;
let a = exports
.f4(&mut store)
.context("calling the `f4` export should succeed")?;
assert_eq!(a, 1337);
// let (a, b) = exports
// .f5(&mut store)
// .context("calling the `f5` export should succeed")?;
// assert_eq!(a, 1);
// assert_eq!(b, 2);
// let (a, b, c) = exports
// .f6(&mut store, 100, 200, 300)
// .context("calling the `f6` export should succeed")?;
// assert_eq!(a, 101);
// assert_eq!(b, 201);
// assert_eq!(c, 301);
Ok(())
}

View File

@@ -0,0 +1,9 @@
f1: func()
f2: func(a: u32)
f3: func(a: u32, b: u32)
f4: func() -> u32
// TODO: should re-enable when re-implemented
//f5: func() -> tuple<u32, u32>
//
//f6: func(a: u32, b: u32, c: u32) -> tuple<u32, u32, u32>

View File

@@ -0,0 +1,83 @@
import * as imports from "imports";
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}
function assertEq(a, b) {
assert(a == b, `assertEq failed: ${a} != ${b}`);
}
export function test_imports() {
// const { f1, f2, f3, f4, f5, f6 } = imports;
const { f1, f2, f3, f4 } = imports;
//
// Testing arguments.
//
f1();
f2(42);
// Min and max `u32`.
f3(0, 4294967295);
//
// Testing returns.
//
{
const a = f4();
assertEq(a, 1337);
}
// {
// const [a, b] = f5();
// assertEq(a, 1);
// assertEq(b, 2);
// }
// {
// const [a, b, c] = f6(100, 200, 300);
// assertEq(a, 101);
// assertEq(b, 201);
// assertEq(c, 301);
// }
}
//
// Testing arguments.
//
export function f1() {}
export function f2(x) {
assertEq(x, 42);
}
export function f3(a, b) {
assertEq(a, 0);
assertEq(b, 4294967295);
}
//
// Testing returns.
//
export function f4() {
return 1337;
}
export function f5() {
return [1, 2];
}
export function f6(a, b, c) {
assertEq(a, 100);
assertEq(b, 200);
assertEq(c, 300);
return [a + 1, b + 1, c + 1];
}

View File

@@ -0,0 +1,7 @@
test-imports: func()
f1: func(l: list<u32>)
f2: func() -> list<u32>
// TODO: should re-enable when re-implemented
// f3: func(a: list<u32>, b: list<u32>) -> tuple<list<u32>, list<u32>>
f4: func(l: list<list<u32>>) -> list<list<u32>>

View File

@@ -0,0 +1,88 @@
use anyhow::Context;
use wit_bindgen_wasmtime::Le;
wit_bindgen_wasmtime::export!("../../tests/runtime/smw_lists/imports.wit");
#[derive(Default)]
pub struct Host {
pub f1_l: Vec<u32>,
pub f2_called: bool,
// pub f3_a: Vec<u32>,
// pub f3_b: Vec<u32>,
pub f4_l: Vec<Vec<u32>>,
}
impl imports::Imports for Host {
fn f1(&mut self, l: &[Le<u32>]) {
self.f1_l = l.iter().map(|le| le.get()).collect();
}
fn f2(&mut self) -> Vec<u32> {
self.f2_called = true;
vec![1, 2, 3]
}
// fn f3(&mut self, a: &[Le<u32>], b: &[Le<u32>]) -> (Vec<u32>, Vec<u32>) {
// self.f3_a = a.iter().map(|le| le.get()).collect();
// self.f3_b = b.iter().map(|le| le.get()).collect();
// (vec![], vec![1, 2, 3])
// }
fn f4(&mut self, l: Vec<&[Le<u32>]>) -> Vec<Vec<u32>> {
self.f4_l = l
.into_iter()
.map(|xs| xs.iter().map(|le| le.get()).collect())
.collect();
vec![vec![], vec![4], vec![5, 6]]
}
}
wit_bindgen_wasmtime::import!("../../tests/runtime/smw_lists/exports.wit");
fn run(wasm: &str) -> anyhow::Result<()> {
let (exports, mut store) = crate::instantiate_smw(
wasm,
|linker| imports::add_to_linker(linker, |cx| -> &mut Host { &mut cx.imports }),
|store, module, linker| {
exports::Exports::instantiate(store, module, linker, |cx| &mut cx.exports)
},
)?;
// Test that the import instance called the functions we made available with
// the expected arguments.
exports.test_imports(&mut store)?;
assert_eq!(store.data().imports.f1_l, vec![1, 2, 3]);
assert!(store.data().imports.f2_called);
// assert_eq!(store.data().imports.f3_a, vec![]);
// assert_eq!(store.data().imports.f3_b, vec![1, 2, 3]);
assert_eq!(store.data().imports.f4_l, vec![vec![], vec![1], vec![2, 3]]);
// Test that the export instance behaves as we expect it to.
exports
.f1(&mut store, &[1, 2, 3])
.context("calling the `f1` export should succeed")?;
let l = exports
.f2(&mut store)
.context("calling the `f2` export should succeed")?;
assert_eq!(l, vec![1, 2, 3]);
// let (a, b) = exports
// .f3(&mut store, &[], &[1, 2, 3])
// .context("calling the `f3` export should succeed")?;
// assert_eq!(a, vec![]);
// assert_eq!(b, vec![1, 2, 3]);
let l = exports
.f4(&mut store, &[&[], &[1], &[2, 3]])
.context("calling the `f4` export should succeed")?;
assert_eq!(l, vec![vec![], vec![4], vec![5, 6]]);
Ok(())
}

View File

@@ -0,0 +1,5 @@
f1: func(l: list<u32>)
f2: func() -> list<u32>
// TODO: should re-enable when re-implemented
// f3: func(a: list<u32>, b: list<u32>) -> tuple<list<u32>, list<u32>>
f4: func(l: list<list<u32>>) -> list<list<u32>>

View File

@@ -0,0 +1,77 @@
import * as imports from "imports";
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}
function assertEq(a, b) {
assert(a == b, `assertEq failed: ${a} != ${b}`);
}
export function test_imports() {
// const { f1, f2, f3, f4 } = imports;
const { f1, f2, f4 } = imports;
f1([1, 2, 3]);
const l = f2();
assertEq(l.length, 3);
assertEq(l[0], 1);
assertEq(l[1], 2);
assertEq(l[2], 3);
// const [a, b] = f3([], [1, 2, 3]);
// assertEq(a.length, 0);
// assertEq(b.length, 3);
// assertEq(b[0], 1);
// assertEq(b[1], 2);
// assertEq(b[2], 3);
const l2 = f4([[], [1], [2, 3]]);
assertEq(l2.length, 3);
assertEq(l2[0].length, 0);
assertEq(l2[1].length, 1);
assertEq(l2[1][0], 4);
assertEq(l2[2].length, 2);
assertEq(l2[2][0], 5);
assertEq(l2[2][1], 6);
}
export function f1(l) {
assertEq(l.length, 3);
assertEq(l[0], 1);
assertEq(l[1], 2);
assertEq(l[2], 3);
}
export function f2() {
return [1, 2, 3];
}
export function f3(a, b) {
assertEq(a.length, 0);
assertEq(b.length, 3);
assertEq(b[0], 1);
assertEq(b[1], 2);
assertEq(b[2], 3);
return [
[],
[1, 2, 3]
];
}
export function f4(l) {
assertEq(l.length, 3);
assertEq(l[0].length, 0);
assertEq(l[1].length, 1);
assertEq(l[1][0], 1);
assertEq(l[2].length, 2);
assertEq(l[2][0], 2);
assertEq(l[2][1], 3);
return [
[],
[4],
[5, 6]
];
}

View File

@@ -0,0 +1,7 @@
test-imports: func()
f1: func(s: string)
f2: func() -> string
// TODO: should re-enable when fixed
//f3: func(a: string, b:string, c: string) -> tuple<string, string, string>

View File

@@ -0,0 +1,75 @@
use anyhow::Context;
wit_bindgen_wasmtime::export!("../../tests/runtime/smw_strings/imports.wit");
#[derive(Default)]
pub struct Host {
pub f1_s: String,
pub f2_called: bool,
// pub f3_a: String,
// pub f3_b: String,
// pub f3_c: String,
}
impl imports::Imports for Host {
fn f1(&mut self, s: &str) {
self.f1_s = s.to_string();
}
fn f2(&mut self) -> String {
self.f2_called = true;
"36 chambers".into()
}
// fn f3(&mut self, a: &str, b: &str, c: &str) -> (String, String, String) {
// self.f3_a = a.into();
// self.f3_b = b.into();
// self.f3_c = c.into();
// (a.into(), b.into(), c.into())
// }
}
wit_bindgen_wasmtime::import!("../../tests/runtime/smw_strings/exports.wit");
fn run(wasm: &str) -> anyhow::Result<()> {
let (exports, mut store) = crate::instantiate_smw(
wasm,
|linker| imports::add_to_linker(linker, |cx| -> &mut Host { &mut cx.imports }),
|store, module, linker| {
exports::Exports::instantiate(store, module, linker, |cx| &mut cx.exports)
},
)?;
// Test that the import instance called the functions we made available with
// the expected arguments.
exports.test_imports(&mut store)?;
assert_eq!(store.data().imports.f1_s, "Hello, WIT!");
assert!(store.data().imports.f2_called, "JS should have called `f2`");
// assert_eq!(store.data().imports.f3_a, "");
// assert_eq!(store.data().imports.f3_b, "🚀");
// assert_eq!(store.data().imports.f3_c, "hello");
// Test that the export instance behaves as we expect it to.
exports
.f1(&mut store, "Hello, WIT!")
.context("calling the `f1` export should succeed")?;
let s = exports
.f2(&mut store)
.context("calling the `f2` export should succeed")?;
assert_eq!(s, "36 chambers");
// let (a, b, c) = exports
// .f3(&mut store, "", "🚀", "hello")
// .context("calling the `f3` export should succeed")?;
// assert_eq!(a, "");
// assert_eq!(b, "🚀");
// assert_eq!(c, "hello");
Ok(())
}

View File

@@ -0,0 +1,4 @@
f1: func(s: string)
f2: func() -> string
// TODO: should re-enable when fixed
//f3: func(a: string, b:string, c: string) -> tuple<string, string, string>

View File

@@ -0,0 +1,40 @@
import * as imports from "imports";
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}
function assertEq(a, b) {
assert(a == b, `assertEq failed: ${a} != ${b}`);
}
export function test_imports() {
const { f1, f2 } = imports;
// const { f1, f2, f3 } = imports;
f1("Hello, WIT!");
const s = f2();
assertEq(s, "36 chambers");
// const [a, b, c] = f3("", "🚀", "hello");
// assertEq(a, "");
// assertEq(b, "🚀");
// assertEq(c, "hello");
}
export function f1(s) {
assertEq(s, "Hello, WIT!");
}
export function f2() {
return "36 chambers";
}
export function f3(a, b, c) {
assertEq(a, "");
assertEq(b, "🚀");
assertEq(c, "hello");
return [a, b, c];
}

View File

@@ -0,0 +1,59 @@
test-imports: func()
/// A union of all of the integral types
union all-integers {
/// Bool is equivalent to a 1 bit integer
/// and is treated that way in some languages
bool,
u8, u16, u32, u64,
s8, s16, s32, s64
}
union all-floats {
float32, float64
}
union all-text {
char, string
}
// Returns the same case as the input but with 1 added
add-one-integer: func(num: all-integers) -> all-integers
// Returns the same case as the input but with 1 added
add-one-float: func(num: all-floats) -> all-floats
// Returns the same case as the input but with the first character replaced
replace-first-char: func(text: all-text, letter: char) -> all-text
// Returns the index of the case provided
identify-integer: func(num: all-integers) -> u8
// Returns the index of the case provided
identify-float: func(num: all-floats) -> u8
// Returns the index of the case provided
identify-text: func(text: all-text) -> u8
union duplicated-s32 {
/// The first s32
s32,
/// The second s32
s32,
/// The third s32
s32
}
// Returns the same case as the input but with 1 added
add-one-duplicated: func(num: duplicated-s32) -> duplicated-s32
// Returns the index of the case provided
identify-duplicated: func(num: duplicated-s32) -> u8
/// A type containing numeric types that are distinct in most languages
union distinguishable-num {
/// A Floating Point Number
float64,
/// A Signed Integer
s64
}
// Returns the same case as the input but with 1 added
add-one-distinguishable-num: func(num: distinguishable-num) -> distinguishable-num
// Returns the index of the case provided
identify-distinguishable-num: func(num: distinguishable-num) -> u8

View File

@@ -0,0 +1,237 @@
from exports.bindings import Exports
from imports.bindings import add_imports_to_linker, Imports
from typing import Union
import exports.bindings as e
import imports.bindings as i
import sys
import wasmtime
class MyImports:
# Simple uses of unions whose inner values all have the same Python representation
def add_one_integer(self, num: i.AllIntegers) -> i.AllIntegers:
# Bool
if isinstance(num, i.AllIntegers0):
assert num.value in (True, False)
return i.AllIntegers0(not num.value)
# The unsigned numbers
elif isinstance(num, i.AllIntegers1):
lower_limit = 0
upper_limit = 2**8
assert lower_limit <= num.value < upper_limit
return i.AllIntegers1(num.value + 1 % upper_limit)
elif isinstance(num, i.AllIntegers2):
lower_limit = 0
upper_limit = 2**16
assert lower_limit <= num.value < upper_limit
return i.AllIntegers2(num.value + 1 % upper_limit)
elif isinstance(num, i.AllIntegers3):
lower_limit = 0
upper_limit = 2**32
assert lower_limit <= num.value < upper_limit
return i.AllIntegers3(num.value + 1 % upper_limit)
elif isinstance(num, i.AllIntegers4):
lower_limit = 0
upper_limit = 2**64
assert lower_limit <= num.value < upper_limit
return i.AllIntegers4(num.value + 1 % upper_limit)
# The signed numbers
elif isinstance(num, i.AllIntegers5):
lower_limit = -2**7
upper_limit = 2**7
assert lower_limit <= num.value < upper_limit
return i.AllIntegers5(num.value + 1 % upper_limit)
elif isinstance(num, i.AllIntegers6):
lower_limit = -2**15
upper_limit = 2**15
assert lower_limit <= num.value < upper_limit
return i.AllIntegers6(num.value + 1 % upper_limit)
elif isinstance(num, i.AllIntegers7):
lower_limit = -2**31
upper_limit = 2**31
assert lower_limit <= num.value < upper_limit
return i.AllIntegers7(num.value + 1 % upper_limit)
elif isinstance(num, i.AllIntegers8):
lower_limit = -2**63
upper_limit = 2**63
assert lower_limit <= num.value < upper_limit
return i.AllIntegers8(num.value + 1 % upper_limit)
else:
raise ValueError("Invalid input value!")
def add_one_float(self, num: i.AllFloats) -> i.AllFloats:
if isinstance(num, i.AllFloats0):
return i.AllFloats0(num.value + 1)
if isinstance(num, i.AllFloats1):
return i.AllFloats1(num.value + 1)
else:
raise ValueError("Invalid input value!")
def replace_first_char(self, text: i.AllText, letter: str) -> i.AllText:
if isinstance(text, i.AllText0):
return i.AllText0(letter)
if isinstance(text, i.AllFloats1):
return i.AllText1(letter + text.value[1:])
else:
raise ValueError("Invalid input value!")
# Identify each case of unions whose inner values all have the same Python representation
def identify_integer(self, num: i.AllIntegers) -> int:
# Bool
if isinstance(num, i.AllIntegers0):
return 0
# The unsigned numbers
elif isinstance(num, i.AllIntegers1):
return 1
elif isinstance(num, i.AllIntegers2):
return 2
elif isinstance(num, i.AllIntegers3):
return 3
elif isinstance(num, i.AllIntegers4):
return 4
# The signed numbers
elif isinstance(num, i.AllIntegers5):
return 5
elif isinstance(num, i.AllIntegers6):
return 6
elif isinstance(num, i.AllIntegers7):
return 7
elif isinstance(num, i.AllIntegers8):
return 8
else:
raise ValueError("Invalid input value!")
def identify_float(self, num: i.AllFloats) -> int:
if isinstance(num, i.AllFloats0):
return 0
if isinstance(num, i.AllFloats1):
return 1
else:
raise ValueError("Invalid input value!")
def identify_text(self, text: i.AllText) -> int:
if isinstance(text, i.AllText0):
return 0
if isinstance(text, i.AllFloats1):
return 1
else:
raise ValueError("Invalid input value!")
# A simple use of a union which contains multiple entries of the same type
def add_one_duplicated(self, num: i.DuplicatedS32) -> i.DuplicatedS32:
if isinstance(num, i.DuplicatedS320):
return i.DuplicatedS320(num.value + 1)
if isinstance(num, i.DuplicatedS321):
return i.DuplicatedS321(num.value + 1)
if isinstance(num, i.DuplicatedS322):
return i.DuplicatedS322(num.value + 1)
else:
raise ValueError("Invalid input value!")
# Identify each case of unions which contains multiple entries of the same type
def identify_duplicated(self, num: i.DuplicatedS32) -> int:
if isinstance(num, i.DuplicatedS320):
return 0
if isinstance(num, i.DuplicatedS321):
return 1
if isinstance(num, i.DuplicatedS322):
return 2
else:
raise ValueError("Invalid input value!")
# A simple use of a union whose cases have distinct Python representations
def add_one_distinguishable_num(self, num: Union[float, int]) -> Union[float, int]:
return num + 1
# Identify each case of unions whose cases have distinct Python representations
def identify_distinguishable_num(self, num: i.DistinguishableNum) -> int:
if isinstance(num, float):
return 0
elif isinstance(num, int):
return 1
else:
raise ValueError("Invalid input value!")
def run(wasm_file: str) -> None:
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, wasm_file)
linker = wasmtime.Linker(store.engine)
linker.define_wasi()
wasi = wasmtime.WasiConfig()
wasi.inherit_stdout()
wasi.inherit_stderr()
store.set_wasi(wasi)
imports = MyImports()
add_imports_to_linker(linker, store, imports)
wasm = Exports(store, linker, module)
# wasm.test_imports(store)
# All-Integers
# Booleans
assert wasm.add_one_integer(store, e.AllIntegers0(False)) == e.AllIntegers0(True)
assert wasm.add_one_integer(store, e.AllIntegers0(True)) == e.AllIntegers0(False)
# Unsigned integers
assert wasm.add_one_integer(store, e.AllIntegers1(0)) == e.AllIntegers1(1)
assert wasm.add_one_integer(store, e.AllIntegers1(2**8-1)) == e.AllIntegers1(0)
assert wasm.add_one_integer(store, e.AllIntegers2(0)) == e.AllIntegers2(1)
assert wasm.add_one_integer(store, e.AllIntegers2(2**16-1)) == e.AllIntegers2(0)
assert wasm.add_one_integer(store, e.AllIntegers3(0)) == e.AllIntegers3(1)
assert wasm.add_one_integer(store, e.AllIntegers3(2**32-1)) == e.AllIntegers3(0)
assert wasm.add_one_integer(store, e.AllIntegers4(0)) == e.AllIntegers4(1)
assert wasm.add_one_integer(store, e.AllIntegers4(2**64-1)) == e.AllIntegers4(0)
# Signed integers
assert wasm.add_one_integer(store, e.AllIntegers5(0)) == e.AllIntegers5(1)
assert wasm.add_one_integer(store, e.AllIntegers5(2**7-1)) == e.AllIntegers5(-2**7)
assert wasm.add_one_integer(store, e.AllIntegers6(0)) == e.AllIntegers6(1)
assert wasm.add_one_integer(store, e.AllIntegers6(2**15-1)) == e.AllIntegers6(-2**15)
assert wasm.add_one_integer(store, e.AllIntegers7(0)) == e.AllIntegers7(1)
assert wasm.add_one_integer(store, e.AllIntegers7(2**31-1)) == e.AllIntegers7(-2**31)
assert wasm.add_one_integer(store, e.AllIntegers8(0)) == e.AllIntegers8(1)
assert wasm.add_one_integer(store, e.AllIntegers8(2**63-1)) == e.AllIntegers8(-2**63)
# All-Floats
assert wasm.add_one_float(store, e.AllFloats0(0.0)) == e.AllFloats0(1.0)
assert wasm.add_one_float(store, e.AllFloats1(0.0)) == e.AllFloats1(1.0)
# All-Text
assert wasm.replace_first_char(store, e.AllText0('a'), 'z') == e.AllText0('z')
assert wasm.replace_first_char(store, e.AllText1('abc'), 'z') == e.AllText1('zbc')
# All-Integers
assert wasm.identify_integer(store, e.AllIntegers0(True)) == 0
assert wasm.identify_integer(store, e.AllIntegers1(0)) == 1
assert wasm.identify_integer(store, e.AllIntegers2(0)) == 2
assert wasm.identify_integer(store, e.AllIntegers3(0)) == 3
assert wasm.identify_integer(store, e.AllIntegers4(0)) == 4
assert wasm.identify_integer(store, e.AllIntegers5(0)) == 5
assert wasm.identify_integer(store, e.AllIntegers6(0)) == 6
assert wasm.identify_integer(store, e.AllIntegers7(0)) == 7
assert wasm.identify_integer(store, e.AllIntegers8(0)) == 8
# All-Floats
assert wasm.identify_float(store, e.AllFloats0(0.0)) == 0
assert wasm.identify_float(store, e.AllFloats1(0.0)) == 1
# All-Text
assert wasm.identify_text(store, e.AllText0('a')) == 0
assert wasm.identify_text(store, e.AllText1('abc')) == 1
# Duplicated
assert wasm.add_one_duplicated(store, e.DuplicatedS320(0)) == e.DuplicatedS320(1)
assert wasm.add_one_duplicated(store, e.DuplicatedS321(1)) == e.DuplicatedS321(2)
assert wasm.add_one_duplicated(store, e.DuplicatedS322(2)) == e.DuplicatedS322(3)
assert wasm.identify_duplicated(store, e.DuplicatedS320(0)) == 0
assert wasm.identify_duplicated(store, e.DuplicatedS321(0)) == 1
assert wasm.identify_duplicated(store, e.DuplicatedS322(0)) == 2
# Distinguishable
assert wasm.add_one_distinguishable_num(store, 0.0) == 1.0
assert wasm.add_one_distinguishable_num(store, 0) == 1
assert wasm.identify_distinguishable_num(store, 0.0) == 0
assert wasm.identify_distinguishable_num(store, 1) == 1
if __name__ == '__main__':
run(sys.argv[1])

View File

@@ -0,0 +1,57 @@
/// A union of all of the integral types
union all-integers {
/// Bool is equivalent to a 1 bit integer
/// and is treated that way in some languages
bool,
u8, u16, u32, u64,
s8, s16, s32, s64
}
union all-floats {
float32, float64
}
union all-text {
char, string
}
// Returns the same case as the input but with 1 added
add-one-integer: func(num: all-integers) -> all-integers
// Returns the same case as the input but with 1 added
add-one-float: func(num: all-floats) -> all-floats
// Returns the same case as the input but with the first character replaced
replace-first-char: func(text: all-text, letter: char) -> all-text
// Returns the index of the case provided
identify-integer: func(num: all-integers) -> u8
// Returns the index of the case provided
identify-float: func(num: all-floats) -> u8
// Returns the index of the case provided
identify-text: func(text: all-text) -> u8
union duplicated-s32 {
/// The first s32
s32,
/// The second s32
s32,
/// The third s32
s32
}
// Returns the same case as the input but with 1 added
add-one-duplicated: func(num: duplicated-s32) -> duplicated-s32
// Returns the index of the case provided
identify-duplicated: func(num: duplicated-s32) -> u8
/// A type containing numeric types that are distinct in most languages
union distinguishable-num {
/// A Floating Point Number
float64,
/// A Signed Integer
s64
}
// Returns the same case as the input but with 1 added
add-one-distinguishable-num: func(num: distinguishable-num) -> distinguishable-num
// Returns the index of the case provided
identify-distinguishable-num: func(num: distinguishable-num) -> u8

View File

@@ -0,0 +1,171 @@
wit_bindgen_rust::import!("../../tests/runtime/unions/imports.wit");
wit_bindgen_rust::export!("../../tests/runtime/unions/exports.wit");
use exports::*;
struct Exports;
impl exports::Exports for Exports {
fn test_imports() {
use imports::*;
// All-Integers
// Booleans
assert!(matches!(add_one_integer(AllIntegers::Bool(false)), AllIntegers::Bool(true)));
assert!(matches!(add_one_integer(AllIntegers::Bool(true)), AllIntegers::Bool(false)));
// Unsigned integers
assert!(matches!(add_one_integer(AllIntegers::U8(0)), AllIntegers::U8(1)));
assert!(matches!(add_one_integer(AllIntegers::U8(u8::MAX)), AllIntegers::U8(0)));
assert!(matches!(add_one_integer(AllIntegers::U16(0)), AllIntegers::U16(1)));
assert!(matches!(add_one_integer(AllIntegers::U16(u16::MAX)), AllIntegers::U16(0)));
assert!(matches!(add_one_integer(AllIntegers::U32(0)), AllIntegers::U32(1)));
assert!(matches!(add_one_integer(AllIntegers::U32(u32::MAX)), AllIntegers::U32(0)));
assert!(matches!(add_one_integer(AllIntegers::U64(0)), AllIntegers::U64(1)));
assert!(matches!(add_one_integer(AllIntegers::U64(u64::MAX)), AllIntegers::U64(0)));
// Signed integers
assert!(matches!(add_one_integer(AllIntegers::I8(0)), AllIntegers::I8(1)));
assert!(matches!(add_one_integer(AllIntegers::I8(i8::MAX)), AllIntegers::I8(i8::MIN)));
assert!(matches!(add_one_integer(AllIntegers::I16(0)), AllIntegers::I16(1)));
assert!(matches!(add_one_integer(AllIntegers::I16(i16::MAX)), AllIntegers::I16(i16::MIN)));
assert!(matches!(add_one_integer(AllIntegers::I32(0)), AllIntegers::I32(1)));
assert!(matches!(add_one_integer(AllIntegers::I32(i32::MAX)), AllIntegers::I32(i32::MIN)));
assert!(matches!(add_one_integer(AllIntegers::I64(0)), AllIntegers::I64(1)));
assert!(matches!(add_one_integer(AllIntegers::I64(i64::MAX)), AllIntegers::I64(i64::MIN)));
// All-Floats
assert!(matches!(add_one_float(AllFloats::F32(0.0)), AllFloats::F32(1.0)));
assert!(matches!(add_one_float(AllFloats::F64(0.0)), AllFloats::F64(1.0)));
// All-Text
assert!(matches!(replace_first_char(AllTextParam::Char('a'), 'z'), AllTextResult::Char('z')));
let rhs = "zbc".to_string();
assert!(matches!(replace_first_char(AllTextParam::String("abc"), 'z'), AllTextResult::String(rhs)));
// All-Integers
assert!(matches!(identify_integer(AllIntegers::Bool(true)), 0));
assert!(matches!(identify_integer(AllIntegers::U8(0)), 1));
assert!(matches!(identify_integer(AllIntegers::U16(0)), 2));
assert!(matches!(identify_integer(AllIntegers::U32(0)), 3));
assert!(matches!(identify_integer(AllIntegers::U64(0)), 4));
assert!(matches!(identify_integer(AllIntegers::I8(0)), 5));
assert!(matches!(identify_integer(AllIntegers::I16(0)), 6));
assert!(matches!(identify_integer(AllIntegers::I32(0)), 7));
assert!(matches!(identify_integer(AllIntegers::I64(0)), 8));
// All-Floats
assert!(matches!(identify_float(AllFloats::F32(0.0)), 0));
assert!(matches!(identify_float(AllFloats::F64(0.0)), 1));
// All-Text
assert!(matches!(identify_text(AllTextParam::Char('a')), 0));
assert!(matches!(identify_text(AllTextParam::String("abc")), 1));
// Duplicated
assert!(matches!(add_one_duplicated(DuplicatedS32::I320(0)), DuplicatedS32::I320(1)));
assert!(matches!(add_one_duplicated(DuplicatedS32::I321(1)), DuplicatedS32::I321(2)));
assert!(matches!(add_one_duplicated(DuplicatedS32::I322(2)), DuplicatedS32::I322(3)));
assert!(matches!(identify_duplicated(DuplicatedS32::I320(0)), 0));
assert!(matches!(identify_duplicated(DuplicatedS32::I321(0)), 1));
assert!(matches!(identify_duplicated(DuplicatedS32::I321(0)), 2));
// Distinguishable
assert!(matches!(add_one_distinguishable_num(DistinguishableNum::F64(0.0)), DistinguishableNum::F64(1.0)));
assert!(matches!(add_one_distinguishable_num(DistinguishableNum::I64(0)), DistinguishableNum::I64(1)));
assert!(matches!(identify_distinguishable_num(DistinguishableNum::F64(0.0)), 0));
assert!(matches!(identify_distinguishable_num(DistinguishableNum::I64(1)), 1));
}
fn add_one_integer(num: AllIntegers) -> AllIntegers {
match num {
// Boolean
AllIntegers::Bool(b) => AllIntegers::Bool(!b),
// Unsigneed Integers
AllIntegers::U8(n) => AllIntegers::U8(n.wrapping_add(1)),
AllIntegers::U16(n) => AllIntegers::U16(n.wrapping_add(1)),
AllIntegers::U32(n) => AllIntegers::U32(n.wrapping_add(1)),
AllIntegers::U64(n) => AllIntegers::U64(n.wrapping_add(1)),
// Signed Integers
AllIntegers::I8(n) => AllIntegers::I8(n.wrapping_add(1)),
AllIntegers::I16(n) => AllIntegers::I16(n.wrapping_add(1)),
AllIntegers::I32(n) => AllIntegers::I32(n.wrapping_add(1)),
AllIntegers::I64(n) => AllIntegers::I64(n.wrapping_add(1)),
}
}
fn add_one_float(num: AllFloats) -> AllFloats {
match num {
AllFloats::F32(n) => AllFloats::F32(n + 1.0),
AllFloats::F64(n) => AllFloats::F64(n + 1.0),
}
}
fn replace_first_char(text: AllText, letter: char) -> AllText {
match text {
AllText::Char(c) => AllText::Char(letter),
AllText::String(s) => AllText::String(format!("{}{}", letter, &s[1..]))
}
}
fn identify_integer(num: AllIntegers) -> u8 {
match num {
// Boolean
AllIntegers::Bool(_b) => 0,
// Unsigneed Integers
AllIntegers::U8(_n) => 1,
AllIntegers::U16(_n) => 2,
AllIntegers::U32(_n) => 3,
AllIntegers::U64(_n) => 4,
// Signed Integers
AllIntegers::I8(_n) => 5,
AllIntegers::I16(_n) => 6,
AllIntegers::I32(_n) => 7,
AllIntegers::I64(_n) => 8,
}
}
fn identify_float(num: AllFloats) -> u8 {
match num {
AllFloats::F32(_n) => 0,
AllFloats::F64(_n) => 1,
}
}
fn identify_text(text: AllText) -> u8 {
match text {
AllText::Char(_c) => 0,
AllText::String(_s) => 1
}
}
fn add_one_duplicated(num: DuplicatedS32) -> DuplicatedS32 {
match num {
DuplicatedS32::I320(n) => DuplicatedS32::I320(n.wrapping_add(1)),
DuplicatedS32::I321(n) => DuplicatedS32::I321(n.wrapping_add(1)),
DuplicatedS32::I322(n) => DuplicatedS32::I322(n.wrapping_add(1)),
}
}
fn identify_duplicated(num: DuplicatedS32) -> u8 {
match num {
DuplicatedS32::I320(_n) => 0,
DuplicatedS32::I321(_n) => 1,
DuplicatedS32::I322(_n) => 2,
}
}
fn add_one_distinguishable_num(num: DistinguishableNum) -> DistinguishableNum {
match num {
DistinguishableNum::F64(n) => DistinguishableNum::F64(n + 1.0),
DistinguishableNum::I64(n) => DistinguishableNum::I64(n.wrapping_add(1)),
}
}
fn identify_distinguishable_num(num: DistinguishableNum) -> u8 {
match num {
DistinguishableNum::F64(_n) => 0,
DistinguishableNum::I64(_n) => 1,
}
}
}

View File

@@ -0,0 +1,30 @@
test-imports: func()
roundtrip-option: func(a: option<float32>) -> option<u8>
roundtrip-result: func(a: expected<u32, float32>) -> expected<float64, u8>
enum e1 { a, b }
roundtrip-enum: func(a: e1) -> e1
invert-bool: func(a: bool) -> bool
variant c1 { a(s32), b(s64) }
variant c2 { a(s32), b(float32) }
variant c3 { a(s32), b(float64) }
variant c4 { a(s64), b(float32) }
variant c5 { a(s64), b(float64) }
variant c6 { a(float32), b(float64) }
type casts = tuple<c1, c2, c3, c4, c5, c6>
variant-casts: func(a: casts) -> casts
variant z1 { a(s32), b }
variant z2 { a(s64), b }
variant z3 { a(float32), b }
variant z4 { a(float64), b }
type zeros = tuple<z1, z2, z3, z4>
variant-zeros: func(a: zeros) -> zeros
type option-typedef = option<u32>
type bool-typedef = bool
type result-typedef = expected<u32, unit>
variant-typedefs: func(a: option-typedef, b: bool-typedef, c: result-typedef)

View File

@@ -0,0 +1,114 @@
from exports.bindings import Exports
from imports.bindings import add_imports_to_linker, Imports
from typing import Optional, Tuple
import exports.bindings as e
import imports.bindings as i
import sys
import wasmtime
class MyImports:
def roundtrip_option(self, a: Optional[float]) -> Optional[int]:
if a:
return int(a)
return None
def roundtrip_result(self, a: i.Expected[int, float]) -> i.Expected[float, int]:
if isinstance(a, i.Ok):
return i.Ok(float(a.value))
return i.Err(int(a.value))
def roundtrip_enum(self, a: i.E1) -> i.E1:
return a
def invert_bool(self, a: bool) -> bool:
return not a
def variant_casts(self, a: i.Casts) -> i.Casts:
return a
def variant_zeros(self, a: i.Zeros) -> i.Zeros:
return a
def variant_typedefs(self, a: i.OptionTypedef, b: i.BoolTypedef, c: i.ResultTypedef) -> None:
pass
def variant_enums(self, a: bool, b: i.Expected[None, None], c: i.MyErrno) -> Tuple[bool, i.Expected[None, None], i.MyErrno]:
assert(a)
assert(isinstance(b, i.Ok))
assert(c == i.MyErrno.SUCCESS)
return (False, i.Err(None), i.MyErrno.A)
def run(wasm_file: str) -> None:
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, wasm_file)
linker = wasmtime.Linker(store.engine)
linker.define_wasi()
wasi = wasmtime.WasiConfig()
wasi.inherit_stdout()
wasi.inherit_stderr()
store.set_wasi(wasi)
imports = MyImports()
add_imports_to_linker(linker, store, imports)
wasm = Exports(store, linker, module)
wasm.test_imports(store)
assert(wasm.roundtrip_option(store, 1.) == 1)
assert(wasm.roundtrip_option(store, None) == None)
assert(wasm.roundtrip_option(store, 2.) == 2)
assert(wasm.roundtrip_result(store, e.Ok(2)) == e.Ok(2))
assert(wasm.roundtrip_result(store, e.Ok(4)) == e.Ok(4))
assert(wasm.roundtrip_result(store, e.Err(5)) == e.Err(5))
assert(wasm.roundtrip_enum(store, e.E1.A) == e.E1.A)
assert(wasm.roundtrip_enum(store, e.E1.B) == e.E1.B)
assert(wasm.invert_bool(store, True) == False)
assert(wasm.invert_bool(store, False) == True)
a1, a2, a3, a4, a5, a6 = wasm.variant_casts(store, (
e.C1A(1),
e.C2A(2),
e.C3A(3),
e.C4A(4),
e.C5A(5),
e.C6A(6.),
))
assert(a1 == e.C1A(1))
assert(a2 == e.C2A(2))
assert(a3 == e.C3A(3))
assert(a4 == e.C4A(4))
assert(a5 == e.C5A(5))
assert(a6 == e.C6A(6))
b1, b2, b3, b4, b5, b6 = wasm.variant_casts(store, (
e.C1B(1),
e.C2B(2),
e.C3B(3),
e.C4B(4),
e.C5B(5),
e.C6B(6.),
))
assert(b1 == e.C1B(1))
assert(b2 == e.C2B(2))
assert(b3 == e.C3B(3))
assert(b4 == e.C4B(4))
assert(b5 == e.C5B(5))
assert(b6 == e.C6B(6))
z1, z2, z3, z4 = wasm.variant_zeros(store, (
e.Z1A(1),
e.Z2A(2),
e.Z3A(3.),
e.Z4A(4.),
))
assert(z1 == e.Z1A(1))
assert(z2 == e.Z2A(2))
assert(z3 == e.Z3A(3))
assert(z4 == e.Z4A(4))
wasm.variant_typedefs(store, None, False, e.Err(None))
if __name__ == '__main__':
run(sys.argv[1])

Some files were not shown because too many files have changed in this diff Show More