Path: blob/main/tests/misc_testsuite/component-model/resources.wast
3080 views
;;! component_model_async = true
;; bare bones "intrinsics work"
(component
(type $r (resource (rep i32)))
(core func $rep (canon resource.rep $r))
(core func $new (canon resource.new $r))
(core func $drop (canon resource.drop $r))
(core module $m
(import "" "rep" (func $rep (param i32) (result i32)))
(import "" "new" (func $new (param i32) (result i32)))
(import "" "drop" (func $drop (param i32)))
(func $start
(local $r i32)
(local.set $r (call $new (i32.const 100)))
(if (i32.ne (local.get $r) (i32.const 1)) (then (unreachable)))
(if (i32.ne (call $rep (local.get $r)) (i32.const 100)) (then (unreachable)))
(call $drop (local.get $r))
)
(start $start)
)
(core instance (instantiate $m
(with "" (instance
(export "rep" (func $rep))
(export "new" (func $new))
(export "drop" (func $drop))
))
))
)
;; cannot call `resource.drop` on a nonexistent resource
(component
(type $r (resource (rep i32)))
(core func $drop (canon resource.drop $r))
(core module $m
(import "" "drop" (func $drop (param i32)))
(func (export "r")
(call $drop (i32.const 0))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "drop" (func $drop))
))
))
(func (export "r") (canon lift (core func $i "r")))
)
(assert_trap (invoke "r") "unknown handle index 0")
;; cannot call `resource.rep` on a nonexistent resource
(component
(type $r (resource (rep i32)))
(core func $rep (canon resource.rep $r))
(core module $m
(import "" "rep" (func $rep (param i32) (result i32)))
(func (export "r")
(drop (call $rep (i32.const 0)))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "rep" (func $rep))
))
))
(func (export "r") (canon lift (core func $i "r")))
)
(assert_trap (invoke "r") "unknown handle index 0")
;; index reuse behavior of handles
(component
(type $r (resource (rep i32)))
(core func $rep (canon resource.rep $r))
(core func $new (canon resource.new $r))
(core func $drop (canon resource.drop $r))
(core module $m
(import "" "rep" (func $rep (param i32) (result i32)))
(import "" "new" (func $new (param i32) (result i32)))
(import "" "drop" (func $drop (param i32)))
(func $start
(local $r1 i32)
(local $r2 i32)
(local $r3 i32)
(local $r4 i32)
;; resources assigned sequentially
(local.set $r1 (call $new (i32.const 100)))
(if (i32.ne (local.get $r1) (i32.const 1)) (then (unreachable)))
(local.set $r2 (call $new (i32.const 200)))
(if (i32.ne (local.get $r2) (i32.const 2)) (then (unreachable)))
(local.set $r3 (call $new (i32.const 300)))
(if (i32.ne (local.get $r3) (i32.const 3)) (then (unreachable)))
;; representations all look good
(if (i32.ne (call $rep (local.get $r1)) (i32.const 100)) (then (unreachable)))
(if (i32.ne (call $rep (local.get $r2)) (i32.const 200)) (then (unreachable)))
(if (i32.ne (call $rep (local.get $r3)) (i32.const 300)) (then (unreachable)))
;; reallocate r2
(call $drop (local.get $r2))
(local.set $r2 (call $new (i32.const 400)))
;; should have reused index 3
(if (i32.ne (local.get $r2) (i32.const 2)) (then (unreachable)))
;; representations all look good
(if (i32.ne (call $rep (local.get $r1)) (i32.const 100)) (then (unreachable)))
(if (i32.ne (call $rep (local.get $r2)) (i32.const 400)) (then (unreachable)))
(if (i32.ne (call $rep (local.get $r3)) (i32.const 300)) (then (unreachable)))
;; deallocate, then reallocate
(call $drop (local.get $r1))
(call $drop (local.get $r2))
(call $drop (local.get $r3))
(local.set $r1 (call $new (i32.const 500)))
(local.set $r2 (call $new (i32.const 600)))
(local.set $r3 (call $new (i32.const 700)))
;; representations all look good
(if (i32.ne (call $rep (local.get $r1)) (i32.const 500)) (then (unreachable)))
(if (i32.ne (call $rep (local.get $r2)) (i32.const 600)) (then (unreachable)))
(if (i32.ne (call $rep (local.get $r3)) (i32.const 700)) (then (unreachable)))
;; indices should be lifo
(if (i32.ne (local.get $r1) (i32.const 3)) (then (unreachable)))
(if (i32.ne (local.get $r2) (i32.const 2)) (then (unreachable)))
(if (i32.ne (local.get $r3) (i32.const 1)) (then (unreachable)))
;; bump one more time
(local.set $r4 (call $new (i32.const 800)))
(if (i32.ne (local.get $r4) (i32.const 4)) (then (unreachable)))
;; deallocate everything
(call $drop (local.get $r1))
(call $drop (local.get $r2))
(call $drop (local.get $r3))
(call $drop (local.get $r4))
)
(start $start)
)
(core instance (instantiate $m
(with "" (instance
(export "rep" (func $rep))
(export "new" (func $new))
(export "drop" (func $drop))
))
))
)
(assert_unlinkable
(component
(import "host" (instance
(export "missing" (type (sub resource)))
))
)
"was not found")
(assert_unlinkable
(component
(import "host" (instance
(export "return-three" (type (sub resource)))
))
)
"expected resource found func")
;; all resources can be uniquely imported
(component
(import "host" (instance
(export "resource1" (type (sub resource)))
(export "resource2" (type (sub resource)))
(export "resource1-again" (type (sub resource)))
))
)
;; equality constraints also work
(component
(import "host" (instance
(export "resource1" (type $r1 (sub resource)))
(export "resource2" (type (sub resource)))
(export "resource1-again" (type (eq $r1)))
))
)
;; equality constraints are checked if resources are supplied
(assert_unlinkable
(component
(import "host" (instance
(export "resource1" (type (sub resource)))
(export "resource2" (type $r1 (sub resource)))
(export "resource1-again" (type (eq $r1)))
))
)
"mismatched resource types")
;; equality constraints mean that types don't need to be supplied
(component
(import "host" (instance
(export "resource1" (type $r1 (sub resource)))
(export "resource2" (type (sub resource)))
(export "this-name-is-not-provided-in-the-wast-harness" (type (eq $r1)))
))
)
;; simple properties of handles
(component
(import "host" (instance $host
(export "resource1" (type $r (sub resource)))
(export "[constructor]resource1" (func (param "r" u32) (result (own $r))))
(export "[static]resource1.assert" (func (param "r" (own $r)) (param "rep" u32)))
))
(alias export $host "resource1" (type $r))
(alias export $host "[constructor]resource1" (func $ctor))
(alias export $host "[static]resource1.assert" (func $assert))
(core func $drop (canon resource.drop $r))
(core func $ctor (canon lower (func $ctor)))
(core func $assert (canon lower (func $assert)))
(core module $m
(import "" "drop" (func $drop (param i32)))
(import "" "ctor" (func $ctor (param i32) (result i32)))
(import "" "assert" (func $assert (param i32 i32)))
(func $start
(local $r1 i32)
(local $r2 i32)
(local.set $r1 (call $ctor (i32.const 100)))
(local.set $r2 (call $ctor (i32.const 200)))
;; assert r1/r2 are sequential
(if (i32.ne (local.get $r1) (i32.const 1)) (then (unreachable)))
(if (i32.ne (local.get $r2) (i32.const 2)) (then (unreachable)))
;; reallocate r1 and it should be reassigned the same index
(call $drop (local.get $r1))
(local.set $r1 (call $ctor (i32.const 300)))
(if (i32.ne (local.get $r1) (i32.const 1)) (then (unreachable)))
;; internal values should match
(call $assert (local.get $r1) (i32.const 300))
(call $assert (local.get $r2) (i32.const 200))
)
(start $start)
)
(core instance (instantiate $m
(with "" (instance
(export "drop" (func $drop))
(export "ctor" (func $ctor))
(export "assert" (func $assert))
))
))
)
;; Using an index that has never been valid is a trap
(component
(import "host" (instance $host
(export "resource1" (type $r (sub resource)))
(export "[static]resource1.assert" (func (param "r" (own $r)) (param "rep" u32)))
))
(alias export $host "resource1" (type $r))
(alias export $host "[static]resource1.assert" (func $assert))
(core func $assert (canon lower (func $assert)))
(core module $m
(import "" "assert" (func $assert (param i32 i32)))
(func (export "f")
(call $assert (i32.const 0) (i32.const 0))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "assert" (func $assert))
))
))
(func (export "f") (canon lift (core func $i "f")))
)
(assert_trap (invoke "f") "unknown handle index")
;; Using an index which was previously valid but no longer valid is also a trap.
(component
(import "host" (instance $host
(export "resource1" (type $r (sub resource)))
(export "[constructor]resource1" (func (param "r" u32) (result (own $r))))
(export "[static]resource1.assert" (func (param "r" (own $r)) (param "rep" u32)))
))
(alias export $host "[constructor]resource1" (func $ctor))
(alias export $host "[static]resource1.assert" (func $assert))
(core func $assert (canon lower (func $assert)))
(core func $ctor (canon lower (func $ctor)))
(core module $m
(import "" "assert" (func $assert (param i32 i32)))
(import "" "ctor" (func $ctor (param i32) (result i32)))
(global $handle (mut i32) i32.const 0)
(func (export "f")
(global.set $handle (call $ctor (i32.const 100)))
(call $assert (global.get $handle) (i32.const 100))
)
(func (export "f2")
(call $assert (global.get $handle) (i32.const 100))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "assert" (func $assert))
(export "ctor" (func $ctor))
))
))
(func (export "f") (canon lift (core func $i "f")))
(func (export "f2") (canon lift (core func $i "f2")))
)
(assert_return (invoke "f"))
(assert_trap (invoke "f2") "unknown handle index")
;; Also invalid to pass a previously valid handle to the drop intrinsic
(component
(import "host" (instance $host
(export "resource1" (type $r (sub resource)))
(export "[constructor]resource1" (func (param "r" u32) (result (own $r))))
))
(alias export $host "resource1" (type $r))
(alias export $host "[constructor]resource1" (func $ctor))
(core func $drop (canon resource.drop $r))
(core func $ctor (canon lower (func $ctor)))
(core module $m
(import "" "drop" (func $drop (param i32)))
(import "" "ctor" (func $ctor (param i32) (result i32)))
(global $handle (mut i32) i32.const 0)
(func (export "f")
(global.set $handle (call $ctor (i32.const 100)))
(call $drop (global.get $handle))
)
(func (export "f2")
(call $drop (global.get $handle))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "ctor" (func $ctor))
(export "drop" (func $drop))
))
))
(func (export "f") (canon lift (core func $i "f")))
(func (export "f2") (canon lift (core func $i "f2")))
)
(assert_return (invoke "f"))
(assert_trap (invoke "f2") "unknown handle index")
;; If an inner component instantiates a resource then an outer component
;; should not implicitly have access to that resource.
(component
(import "host" (instance $host
(export "resource1" (type $r (sub resource)))
(export "[constructor]resource1" (func (param "r" u32) (result (own $r))))
))
;; an inner component which upon instantiation will invoke the constructor,
;; assert that it's zero, and then forget about it.
(component $inner
(import "host" (instance $host
(export "resource1" (type $r (sub resource)))
(export "[constructor]resource1" (func (param "r" u32) (result (own $r))))
))
(alias export $host "[constructor]resource1" (func $ctor))
(core func $ctor (canon lower (func $ctor)))
(core module $m
(import "" "ctor" (func $ctor (param i32) (result i32)))
(func $start
(if (i32.ne (call $ctor (i32.const 100)) (i32.const 0)) (then (unreachable)))
)
)
(core instance $i (instantiate $m
(with "" (instance (export "ctor" (func $ctor))))
))
)
(instance $i (instantiate $inner (with "host" (instance $host))))
;; the rest of this component which is a single function that invokes `drop`
;; for index 0. The index 0 should be valid within the above component, but
;; it is not valid within this component
(alias export $host "resource1" (type $r))
(core func $drop (canon resource.drop $r))
(core module $m
(import "" "drop" (func $drop (param i32)))
(func (export "f")
(call $drop (i32.const 0))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "drop" (func $drop))
))
))
(func (export "f") (canon lift (core func $i "f")))
)
(assert_trap (invoke "f") "unknown handle index")
;; Same as the above test, but for resources defined within a component
(component
(component $inner
(type $r (resource (rep i32)))
(core func $ctor (canon resource.new $r))
(core module $m
(import "" "ctor" (func $ctor (param i32) (result i32)))
(func $start
(if (i32.ne (call $ctor (i32.const 100)) (i32.const 1)) (then (unreachable)))
)
(start $start)
)
(core instance $i (instantiate $m
(with "" (instance (export "ctor" (func $ctor))))
))
(export "r" (type $r))
)
(instance $i (instantiate $inner))
;; the rest of this component which is a single function that invokes `drop`
;; for index 2. The index 2 should be valid within the above component, but
;; it is not valid within this component
(alias export $i "r" (type $r))
(core func $drop (canon resource.drop $r))
(core module $m
(import "" "drop" (func $drop (param i32)))
(func (export "f")
(call $drop (i32.const 2))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "drop" (func $drop))
))
))
(func (export "f") (canon lift (core func $i "f")))
)
(assert_trap (invoke "f") "unknown handle index 2")
;; Each instantiation of a component generates a unique resource type, so
;; allocating in one component and deallocating in another should fail.
(component
(component $inner
(type $r (resource (rep i32)))
(core func $ctor (canon resource.new $r))
(core func $drop (canon resource.drop $r))
(core module $m
(import "" "ctor" (func $ctor (param i32) (result i32)))
(import "" "drop" (func $drop (param i32)))
(func (export "alloc")
(if (i32.ne (call $ctor (i32.const 100)) (i32.const 1)) (then (unreachable)))
)
(func (export "dealloc")
(call $drop (i32.const 1))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "ctor" (func $ctor))
(export "drop" (func $drop))
))
))
(func (export "alloc") (canon lift (core func $i "alloc")))
(func (export "dealloc") (canon lift (core func $i "dealloc")))
)
(instance $i1 (instantiate $inner))
(instance $i2 (instantiate $inner))
(alias export $i1 "alloc" (func $alloc_in_1))
(alias export $i1 "dealloc" (func $dealloc_in_1))
(alias export $i2 "alloc" (func $alloc_in_2))
(alias export $i2 "dealloc" (func $dealloc_in_2))
(export "alloc-in1" (func $alloc_in_1))
(export "dealloc-in1" (func $dealloc_in_1))
(export "alloc-in2" (func $alloc_in_2))
(export "dealloc-in2" (func $dealloc_in_2))
)
(assert_return (invoke "alloc-in1"))
(assert_return (invoke "dealloc-in1"))
(assert_return (invoke "alloc-in1"))
(assert_return (invoke "alloc-in2"))
(assert_return (invoke "dealloc-in2"))
(assert_trap (invoke "dealloc-in2") "unknown handle index")
;; Same as above, but the same host resource type is imported into a
;; component that is instantiated twice. Each component instance should
;; receive different tables tracking resources so a resource allocated in one
;; should not be visible in the other.
(component
(import "host" (instance $host
(export "resource1" (type $r (sub resource)))
(export "[constructor]resource1" (func (param "r" u32) (result (own $r))))
))
(alias export $host "resource1" (type $r))
(alias export $host "[constructor]resource1" (func $ctor))
(component $inner
(import "r" (type $r (sub resource)))
(import "[constructor]r" (func $ctor (param "r" u32) (result (own $r))))
(core func $ctor (canon lower (func $ctor)))
(core func $drop (canon resource.drop $r))
(core module $m
(import "" "ctor" (func $ctor (param i32) (result i32)))
(import "" "drop" (func $drop (param i32)))
(func (export "alloc")
(if (i32.ne (call $ctor (i32.const 100)) (i32.const 1)) (then (unreachable)))
)
(func (export "dealloc")
(call $drop (i32.const 1))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "ctor" (func $ctor))
(export "drop" (func $drop))
))
))
(func (export "alloc") (canon lift (core func $i "alloc")))
(func (export "dealloc") (canon lift (core func $i "dealloc")))
)
(instance $i1 (instantiate $inner
(with "r" (type $r))
(with "[constructor]r" (func $ctor))
))
(instance $i2 (instantiate $inner
(with "r" (type $r))
(with "[constructor]r" (func $ctor))
))
(alias export $i1 "alloc" (func $alloc_in_1))
(alias export $i1 "dealloc" (func $dealloc_in_1))
(alias export $i2 "alloc" (func $alloc_in_2))
(alias export $i2 "dealloc" (func $dealloc_in_2))
(export "alloc-in1" (func $alloc_in_1))
(export "dealloc-in1" (func $dealloc_in_1))
(export "alloc-in2" (func $alloc_in_2))
(export "dealloc-in2" (func $dealloc_in_2))
)
(assert_return (invoke "alloc-in1"))
(assert_return (invoke "dealloc-in1"))
(assert_return (invoke "alloc-in1"))
(assert_return (invoke "alloc-in2"))
(assert_return (invoke "dealloc-in2"))
(assert_trap (invoke "dealloc-in2") "unknown handle index")
;; Multiple copies of intrinsics all work
(component
(type $r (resource (rep i32)))
(core func $new1 (canon resource.new $r))
(core func $new2 (canon resource.new $r))
(core func $drop1 (canon resource.drop $r))
(core func $drop2 (canon resource.drop $r))
(core module $m
(import "" "new1" (func $new1 (param i32) (result i32)))
(import "" "new2" (func $new2 (param i32) (result i32)))
(import "" "drop1" (func $drop1 (param i32)))
(import "" "drop2" (func $drop2 (param i32)))
(func $start
;; 2x2 matrix of pairing new/drop
(call $drop1 (call $new1 (i32.const 101)))
(call $drop2 (call $new1 (i32.const 102)))
(call $drop1 (call $new2 (i32.const 103)))
(call $drop2 (call $new2 (i32.const 104)))
;; should be referencing the same namespace
(if (i32.ne (call $new1 (i32.const 105)) (i32.const 1)) (then (unreachable)))
(if (i32.ne (call $new2 (i32.const 105)) (i32.const 2)) (then (unreachable)))
;; use different drops out of order
(call $drop2 (i32.const 1))
(call $drop1 (i32.const 2))
)
(start $start)
)
(core instance (instantiate $m
(with "" (instance
(export "new1" (func $new1))
(export "new2" (func $new2))
(export "drop1" (func $drop1))
(export "drop2" (func $drop2))
))
))
)
;; u32::MAX isn't special in some weird way, it's just probably always invalid
;; because that's a lot of handles.
(component
(type $r (resource (rep i32)))
(core func $drop (canon resource.drop $r))
(core module $m
(import "" "drop" (func $drop (param i32)))
(func (export "f")
(call $drop (i32.const 0xffffffff))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "drop" (func $drop))
))
))
(func (export "f") (canon lift (core func $i "f")))
)
(assert_trap (invoke "f") "unknown handle index")
;; Test behavior of running a destructor for local resources
(component
(core module $m1
(global $drops (mut i32) i32.const 0)
(global $last_drop (mut i32) i32.const -1)
(func (export "dtor") (param i32)
(global.set $drops (i32.add (global.get $drops) (i32.const 1)))
(global.set $last_drop (local.get 0))
)
(func (export "drops") (result i32) global.get $drops)
(func (export "last-drop") (result i32) global.get $last_drop)
)
(core instance $i1 (instantiate $m1))
(type $r1 (resource (rep i32)))
(type $r2 (resource (rep i32) (dtor (func $i1 "dtor"))))
(core func $drop1 (canon resource.drop $r1))
(core func $drop2 (canon resource.drop $r2))
(core func $new1 (canon resource.new $r1))
(core func $new2 (canon resource.new $r2))
(core module $m2
(import "" "drop1" (func $drop1 (param i32)))
(import "" "drop2" (func $drop2 (param i32)))
(import "" "new1" (func $new1 (param i32) (result i32)))
(import "" "new2" (func $new2 (param i32) (result i32)))
(import "i1" "drops" (func $drops (result i32)))
(import "i1" "last-drop" (func $last-drop (result i32)))
(func $start
(local $r1 i32)
(local $r2 i32)
(local.set $r1 (call $new1 (i32.const 100)))
(local.set $r2 (call $new2 (i32.const 200)))
;; indexes start at 1 and while they have distinct types they should be
;; within the same table.
(if (i32.ne (local.get $r1) (i32.const 1)) (then (unreachable)))
(if (i32.ne (local.get $r2) (i32.const 2)) (then (unreachable)))
;; nothing should be dropped yet
(if (i32.ne (call $drops) (i32.const 0)) (then (unreachable)))
(if (i32.ne (call $last-drop) (i32.const -1)) (then (unreachable)))
;; dropping a resource without a destructor is ok, but shouldn't tamper
;; with anything.
(call $drop1 (local.get $r1))
(if (i32.ne (call $drops) (i32.const 0)) (then (unreachable)))
(if (i32.ne (call $last-drop) (i32.const -1)) (then (unreachable)))
;; drop r2 which should record a drop and additionally record the private
;; representation value which was dropped
(call $drop2 (local.get $r2))
(if (i32.ne (call $drops) (i32.const 1)) (then (unreachable)))
(if (i32.ne (call $last-drop) (i32.const 200)) (then (unreachable)))
;; do it all over again
(local.set $r2 (call $new2 (i32.const 300)))
(call $drop2 (local.get $r2))
(if (i32.ne (call $drops) (i32.const 2)) (then (unreachable)))
(if (i32.ne (call $last-drop) (i32.const 300)) (then (unreachable)))
)
(start $start)
)
(core instance $i2 (instantiate $m2
(with "" (instance
(export "drop1" (func $drop1))
(export "drop2" (func $drop2))
(export "new1" (func $new1))
(export "new2" (func $new2))
))
(with "i1" (instance $i1))
))
)
;; Test dropping a host resource
(component
(import "host" (instance $host
(export "resource1" (type $r (sub resource)))
(export "[constructor]resource1" (func (param "r" u32) (result (own $r))))
(export "[static]resource1.last-drop" (func (result u32)))
(export "[static]resource1.drops" (func (result u32)))
))
(alias export $host "resource1" (type $r))
(alias export $host "[constructor]resource1" (func $ctor))
(alias export $host "[static]resource1.last-drop" (func $last-drop))
(alias export $host "[static]resource1.drops" (func $drops))
(core func $drop (canon resource.drop $r))
(core func $ctor (canon lower (func $ctor)))
(core func $last-drop (canon lower (func $last-drop)))
(core func $drops (canon lower (func $drops)))
(core module $m
(import "" "drop" (func $drop (param i32)))
(import "" "ctor" (func $ctor (param i32) (result i32)))
(import "" "last-drop" (func $last-drop (result i32)))
(import "" "drops" (func $raw-drops (result i32)))
(global $init-drop-cnt (mut i32) i32.const 0)
(func $drops (result i32)
(i32.sub (call $raw-drops) (global.get $init-drop-cnt))
)
(func $start
(local $r1 i32)
(global.set $init-drop-cnt (call $raw-drops))
(local.set $r1 (call $ctor (i32.const 100)))
;; should be no drops yet
(if (i32.ne (call $drops) (i32.const 0)) (then (unreachable)))
;; should count a drop
(call $drop (local.get $r1))
(if (i32.ne (call $drops) (i32.const 1)) (then (unreachable)))
(if (i32.ne (call $last-drop) (i32.const 100)) (then (unreachable)))
;; do it again to be sure
(local.set $r1 (call $ctor (i32.const 200)))
(call $drop (local.get $r1))
(if (i32.ne (call $drops) (i32.const 2)) (then (unreachable)))
(if (i32.ne (call $last-drop) (i32.const 200)) (then (unreachable)))
)
(start $start)
)
(core instance (instantiate $m
(with "" (instance
(export "drop" (func $drop))
(export "ctor" (func $ctor))
(export "last-drop" (func $last-drop))
(export "drops" (func $drops))
))
))
)
;; Test some bare-bones basics of borrowed resources
(component
(import "host" (instance $host
(export "resource1" (type $r (sub resource)))
(export "[constructor]resource1" (func (param "r" u32) (result (own $r))))
(export "[method]resource1.simple" (func (param "self" (borrow $r)) (param "rep" u32)))
(export "[method]resource1.take-borrow" (func (param "self" (borrow $r)) (param "b" (borrow $r))))
(export "[method]resource1.take-own" (func (param "self" (borrow $r)) (param "b" (own $r))))
))
(alias export $host "resource1" (type $r))
(alias export $host "[constructor]resource1" (func $ctor))
(alias export $host "[method]resource1.simple" (func $simple))
(alias export $host "[method]resource1.take-borrow" (func $take-borrow))
(alias export $host "[method]resource1.take-own" (func $take-own))
(core func $drop (canon resource.drop $r))
(core func $ctor (canon lower (func $ctor)))
(core func $simple (canon lower (func $simple)))
(core func $take-own (canon lower (func $take-own)))
(core func $take-borrow (canon lower (func $take-borrow)))
(core module $m
(import "" "drop" (func $drop (param i32)))
(import "" "ctor" (func $ctor (param i32) (result i32)))
(import "" "simple" (func $simple (param i32 i32)))
(import "" "take-own" (func $take-own (param i32 i32)))
(import "" "take-borrow" (func $take-borrow (param i32 i32)))
(func $start
(local $r1 i32)
(local $r2 i32)
(local.set $r1 (call $ctor (i32.const 100)))
(local.set $r2 (call $ctor (i32.const 200)))
(call $simple (local.get $r1) (i32.const 100))
(call $simple (local.get $r1) (i32.const 100))
(call $simple (local.get $r2) (i32.const 200))
(call $simple (local.get $r1) (i32.const 100))
(call $simple (local.get $r2) (i32.const 200))
(call $simple (local.get $r2) (i32.const 200))
(call $drop (local.get $r1))
(call $drop (local.get $r2))
(local.set $r1 (call $ctor (i32.const 200)))
(local.set $r2 (call $ctor (i32.const 300)))
(call $take-borrow (local.get $r1) (local.get $r2))
(call $take-borrow (local.get $r2) (local.get $r1))
(call $take-borrow (local.get $r1) (local.get $r1))
(call $take-borrow (local.get $r2) (local.get $r2))
(call $take-own (local.get $r1) (call $ctor (i32.const 400)))
(call $take-own (local.get $r2) (call $ctor (i32.const 500)))
(call $take-own (local.get $r2) (local.get $r1))
(call $drop (local.get $r2))
;; table should be empty at this point, so a fresh allocation should get
;; index 2
(if (i32.ne (call $ctor (i32.const 600)) (i32.const 1)) (then (unreachable)))
)
(start $start)
)
(core instance (instantiate $m
(with "" (instance
(export "drop" (func $drop))
(export "ctor" (func $ctor))
(export "simple" (func $simple))
(export "take-own" (func $take-own))
(export "take-borrow" (func $take-borrow))
))
))
)
;; Cannot pass out an owned resource when it's borrowed by the same call
(component
(import "host" (instance $host
(export "resource1" (type $r (sub resource)))
(export "[constructor]resource1" (func (param "r" u32) (result (own $r))))
(export "[method]resource1.take-own" (func (param "self" (borrow $r)) (param "b" (own $r))))
))
(alias export $host "resource1" (type $r))
(alias export $host "[constructor]resource1" (func $ctor))
(alias export $host "[method]resource1.take-own" (func $take-own))
(core func $drop (canon resource.drop $r))
(core func $ctor (canon lower (func $ctor)))
(core func $take-own (canon lower (func $take-own)))
(core module $m
(import "" "drop" (func $drop (param i32)))
(import "" "ctor" (func $ctor (param i32) (result i32)))
(import "" "take-own" (func $take-own (param i32 i32)))
(func (export "f")
(local $r i32)
(local.set $r (call $ctor (i32.const 100)))
(call $take-own (local.get $r) (local.get $r))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "drop" (func $drop))
(export "ctor" (func $ctor))
(export "take-own" (func $take-own))
))
))
(func (export "f") (canon lift (core func $i "f")))
)
(assert_trap (invoke "f") "cannot remove owned resource while borrowed")
;; Borrows must actually exist
(component
(import "host" (instance $host
(export "resource1" (type $r (sub resource)))
(export "[method]resource1.simple" (func (param "self" (borrow $r)) (param "b" u32)))
))
(alias export $host "resource1" (type $r))
(alias export $host "[method]resource1.simple" (func $simple))
(core func $drop (canon resource.drop $r))
(core func $simple (canon lower (func $simple)))
(core module $m
(import "" "drop" (func $drop (param i32)))
(import "" "simple" (func $simple (param i32 i32)))
(func (export "f")
(call $simple (i32.const 0) (i32.const 0))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "drop" (func $drop))
(export "simple" (func $simple))
))
))
(func (export "f") (canon lift (core func $i "f")))
)
(assert_trap (invoke "f") "unknown handle index 0")
(component
(component $A
(type $t' (resource (rep i32)))
(export $t "t" (type $t'))
(core func $ctor (canon resource.new $t))
(core func $dtor (canon resource.drop $t))
(core func $rep (canon resource.rep $t))
(core module $m
(import "" "dtor" (func $dtor (param i32)))
(import "" "rep" (func $rep (param i32) (result i32)))
(func (export "[method]t.assert") (param i32 i32)
(if (i32.ne (local.get 0) (local.get 1)) (then (unreachable)))
)
(func (export "[static]t.assert-own") (param i32 i32)
(if (i32.ne (call $rep (local.get 0)) (local.get 1)) (then (unreachable)))
(call $dtor (local.get 0))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "dtor" (func $dtor))
(export "rep" (func $rep))
))
))
(func (export "[constructor]t") (param "x" u32) (result (own $t))
(canon lift (core func $ctor)))
(func (export "[method]t.assert") (param "self" (borrow $t)) (param "x" u32)
(canon lift (core func $i "[method]t.assert")))
(func (export "[static]t.assert-own") (param "self" (own $t)) (param "x" u32)
(canon lift (core func $i "[static]t.assert-own")))
)
(instance $a (instantiate $A))
(component $B
(import "a" (instance $i
(export "t" (type $t (sub resource)))
(export "[constructor]t" (func (param "x" u32) (result (own $t))))
(export "[method]t.assert" (func (param "self" (borrow $t)) (param "x" u32)))
(export "[static]t.assert-own" (func (param "self" (own $t)) (param "x" u32)))
))
(alias export $i "t" (type $t))
(alias export $i "[constructor]t" (func $ctor))
(alias export $i "[method]t.assert" (func $assert-borrow))
(alias export $i "[static]t.assert-own" (func $assert-own))
(core func $ctor (canon lower (func $ctor)))
(core func $dtor (canon resource.drop $t))
(core func $assert-own (canon lower (func $assert-own)))
(core func $assert-borrow (canon lower (func $assert-borrow)))
(core module $m
(import "" "ctor" (func $ctor (param i32) (result i32)))
(import "" "dtor" (func $dtor (param i32)))
(import "" "assert-own" (func $assert-own (param i32 i32)))
(import "" "assert-borrow" (func $assert-borrow (param i32 i32)))
(func (export "f")
(local $r1 i32)
(local $r2 i32)
(local.set $r1 (call $ctor (i32.const 100)))
(local.set $r2 (call $ctor (i32.const 200)))
(if (i32.ne (local.get $r1) (i32.const 1)) (then (unreachable)))
(if (i32.ne (local.get $r2) (i32.const 2)) (then (unreachable)))
(call $assert-borrow (local.get $r2) (i32.const 200))
(call $assert-borrow (local.get $r1) (i32.const 100))
(call $assert-own (local.get $r2) (i32.const 200))
(call $dtor (local.get $r1))
)
)
(core instance $i (instantiate $m
(with "" (instance
(export "ctor" (func $ctor))
(export "dtor" (func $dtor))
(export "assert-own" (func $assert-own))
(export "assert-borrow" (func $assert-borrow))
))
))
(func (export "f") (canon lift (core func $i "f")))
)
(instance $b (instantiate $B (with "a" (instance $a))))
(export "f" (func $b "f"))
)
(assert_return (invoke "f"))
;; Test destructor behavior when using the wrong resource type
(component definition $C
(type $r1 (resource (rep i32)))
(type $r2 (resource (rep i32)))
(core func $drop1 (canon resource.drop $r1))
(core func $drop2 (canon resource.drop $r2))
(core func $new1 (canon resource.new $r1))
(core func $new2 (canon resource.new $r2))
(core module $m2
(import "" "drop1" (func $drop1 (param i32)))
(import "" "drop2" (func $drop2 (param i32)))
(import "" "new1" (func $new1 (param i32) (result i32)))
(import "" "new2" (func $new2 (param i32) (result i32)))
(func (export "drop-r1-as-r2") (call $drop2 (call $new1 (i32.const 100))))
(func (export "return-r1-as-r2") (result i32) (call $new1 (i32.const 100)))
)
(core instance $i2 (instantiate $m2
(with "" (instance
(export "drop1" (func $drop1))
(export "drop2" (func $drop2))
(export "new1" (func $new1))
(export "new2" (func $new2))
))
))
(export $r2' "r2" (type $r2))
(func (export "drop-r1-as-r2") (canon lift (core func $i2 "drop-r1-as-r2")))
(func (export "return-r1-as-r2") (result (own $r2')) (canon lift (core func $i2 "return-r1-as-r2")))
)
(component instance $C1 $C)
(assert_trap (invoke "drop-r1-as-r2") "handle index 1 used with the wrong type, expected guest-defined resource but found a different guest-defined resource")
(component instance $C1 $C)
(assert_trap (invoke "return-r1-as-r2") "handle index 1 used with the wrong type, expected guest-defined resource but found a different guest-defined resource")
;; Test that `resource.rep` is exempt from may-leave checks
(component
(type $r (resource (rep i32)))
(core func $resource.new (canon resource.new $r))
(core func $resource.rep (canon resource.rep $r))
(core module $DM
(import "" "resource.new" (func $resource.new (param i32) (result i32)))
(import "" "resource.rep" (func $resource.rep (param i32) (result i32)))
(global $g (mut i32) (i32.const 0))
(func (export "run")
(global.set $g (call $resource.new (i32.const 42)))
)
(func (export "post-return")
(i32.eq
(call $resource.rep (global.get $g))
(i32.const 42))
if return end
unreachable
)
)
(core instance $dm (instantiate $DM (with "" (instance
(export "resource.new" (func $resource.new))
(export "resource.rep" (func $resource.rep))
))))
(func (export "run")
(canon lift (core func $dm "run") (post-return (func $dm "post-return"))))
)
(assert_return (invoke "run"))