Files
wasmtime/tests/all/component_model.rs
Alex Crichton 3339dd1f01 Implement the post-return attribute (#4297)
This commit implements the `post-return` feature of the canonical ABI in
the component model. This attribute is an optionally-specified function
which is to be executed after the return value has been processed by the
caller to optionally clean-up the return value. This enables, for
example, returning an allocated string and the host then knows how to
clean it up to prevent memory leaks in the original module.

The API exposed in this PR changes the prior `TypedFunc::call` API in
behavior but not in its signature. Previously the `TypedFunc::call`
method would set the `may_enter` flag on the way out, but now that
operation is deferred until a new `TypedFunc::post_return` method is
called. This means that once a method on an instance is invoked then
nothing else can be done on the instance until the `post_return` method
is called. Note that the method must be called irrespective of whether
the `post-return` canonical ABI option was specified or not. Internally
wasm will be invoked if necessary.

This is a pretty wonky and unergonomic API to work with. For now I
couldn't think of a better alternative that improved on the ergonomics.
In the theory that the raw Wasmtime bindings for a component may not be
used all that heavily (instead `wit-bindgen` would largely be used) I'm
hoping that this isn't too much of an issue in the future.

cc #4185
2022-06-23 14:36:21 -05:00

133 lines
3.6 KiB
Rust

use anyhow::Result;
use wasmtime::component::Component;
use wasmtime::{Config, Engine};
mod func;
mod import;
mod nested;
mod post_return;
// A simple bump allocator which can be used with modules
const REALLOC_AND_FREE: &str = r#"
(global $last (mut i32) (i32.const 8))
(func $realloc (export "realloc")
(param $old_ptr i32)
(param $old_size i32)
(param $align i32)
(param $new_size i32)
(result i32)
;; Test if the old pointer is non-null
local.get $old_ptr
if
;; If the old size is bigger than the new size then
;; this is a shrink and transparently allow it
local.get $old_size
local.get $new_size
i32.gt_u
if
local.get $old_ptr
return
end
;; ... otherwise this is unimplemented
unreachable
end
;; align up `$last`
(global.set $last
(i32.and
(i32.add
(global.get $last)
(i32.add
(local.get $align)
(i32.const -1)))
(i32.xor
(i32.add
(local.get $align)
(i32.const -1))
(i32.const -1))))
;; save the current value of `$last` as the return value
global.get $last
;; ensure anything necessary is set to valid data by spraying a bit
;; pattern that is invalid
global.get $last
i32.const 0xde
local.get $new_size
memory.fill
;; bump our pointer
(global.set $last
(i32.add
(global.get $last)
(local.get $new_size)))
)
"#;
fn engine() -> Engine {
let mut config = Config::new();
config.wasm_component_model(true);
Engine::new(&config).unwrap()
}
#[test]
fn components_importing_modules() -> Result<()> {
let engine = engine();
// FIXME: these components should actually get instantiated in `*.wast`
// tests once supplying imports has actually been implemented.
Component::new(
&engine,
r#"
(component
(import "" (core module))
)
"#,
)?;
Component::new(
&engine,
r#"
(component
(import "" (core module $m1
(import "" "" (func))
(import "" "x" (global i32))
(export "a" (table 1 funcref))
(export "b" (memory 1))
(export "c" (func (result f32)))
(export "d" (global i64))
))
(core module $m2
(func (export ""))
(global (export "x") i32 i32.const 0)
)
(core instance $i2 (instantiate (module $m2)))
(core instance $i1 (instantiate (module $m1) (with "" (instance $i2))))
(core module $m3
(import "mod" "1" (memory 1))
(import "mod" "2" (table 1 funcref))
(import "mod" "3" (global i64))
(import "mod" "4" (func (result f32)))
)
(core instance $i3 (instantiate (module $m3)
(with "mod" (instance
(export "1" (memory $i1 "b"))
(export "2" (table $i1 "a"))
(export "3" (global $i1 "d"))
(export "4" (func $i1 "c"))
))
))
)
"#,
)?;
Ok(())
}