Source code

Revision control

Copy as Markdown

Other Tools

// |jit-test| skip-if: !wasmStackSwitchingEnabled()
// Test exception handling interactions with stack switching and GC.
// Exception thrown after suspend/resume with GC.
{
gczeal(14, 1);
let { init, step, resumeAndCatch, getResult } = wasmEvalText(`(module
(type $s (struct (field i32)))
(type $ft (func))
(type $ct (cont $ft))
(tag $tag)
(tag $exn (param i32 i32))
(global $k (mut (ref null $ct)) (ref.null $ct))
(global $r (mut i32) (i32.const 0))
(func $f (type $ft)
(local $a (ref null $s))
(local $b (ref null $s))
(local.set $a (struct.new $s (i32.const 111)))
(local.set $b (struct.new $s (i32.const 222)))
suspend $tag
(throw $exn
(struct.get $s 0 (local.get $a))
(struct.get $s 0 (local.get $b)))
)
(elem declare func $f)
(func (export "init")
(global.set $k (cont.new $ct (ref.func $f)))
)
(func (export "step")
(block (result (ref $ct))
(global.get $k) resume $ct (on $tag 0) return
)
global.set $k
)
(func (export "resumeAndCatch")
try
(global.get $k) resume $ct
catch $exn
(global.set $r (i32.add))
end
)
(func (export "getResult") (result i32) global.get $r)
)`).exports;
init();
step();
gc();
resumeAndCatch();
assertEq(getResult(), 333);
}
// JS exception from import on continuation stack.
{
gczeal(2, 5);
let error_obj = {secret: 12345, data: new Array(50).fill("test")};
let caught = null;
let { run } = wasmEvalText(`(module
(import "env" "throwJS" (func $throwJS))
(type $ft (func))
(type $ct (cont $ft))
(func $f (type $ft) call $throwJS)
(elem declare func $f)
(func (export "run")
ref.func $f
cont.new $ct
resume $ct
)
)`, { env: {
throwJS: () => { throw error_obj; }
}}).exports;
try {
run();
} catch (e) {
caught = e;
}
assertEq(caught === error_obj, true);
assertEq(caught.secret, 12345);
assertEq(caught.data.length, 50);
}
// Nested resume with exception propagation across stack boundaries.
{
gczeal(14, 1);
let { run, getResult } = wasmEvalText(`(module
(type $ft (func))
(type $ct (cont $ft))
(tag $tag)
(tag $exn (param i32))
(global $r (mut i32) (i32.const 0))
(func $inner (type $ft)
suspend $tag
(throw $exn (i32.const 42))
)
(func $outer (type $ft)
(local $inner_k (ref null $ct))
(local.set $inner_k (cont.new $ct (ref.func $inner)))
(block (result (ref $ct))
(local.get $inner_k)
resume $ct (on $tag 0)
unreachable
)
(local.set $inner_k)
suspend $tag
try
(local.get $inner_k)
resume $ct
catch $exn
(global.set $r)
end
)
(elem declare func $inner $outer)
(func (export "run")
(local $outer_k (ref null $ct))
(local.set $outer_k (cont.new $ct (ref.func $outer)))
(block (result (ref $ct))
(local.get $outer_k)
resume $ct (on $tag 0)
unreachable
)
(local.set $outer_k)
(local.get $outer_k)
resume $ct
)
(func (export "getResult") (result i32) global.get $r)
)`).exports;
run();
assertEq(getResult(), 42);
}
// Exception with unmatched handlers propagates to caller of resume.
{
gczeal(14, 1);
let { run } = wasmEvalText(`(module
(type $ft (func))
(type $ct (cont $ft))
(tag $tag1)
(tag $tag2)
(tag $tag3)
(tag $exn (param i32))
(func $f (type $ft)
(throw $exn (i32.const 99))
)
(elem declare func $f)
(func (export "run") (result i32)
try (result i32)
(block (result (ref $ct))
(block (result (ref $ct))
(block (result (ref $ct))
ref.func $f
cont.new $ct
resume $ct (on $tag1 0) (on $tag2 1) (on $tag3 2)
unreachable
)
unreachable
)
unreachable
)
drop
unreachable
catch $exn
end
)
)`).exports;
assertEq(run(), 99);
}