// Tests for Wasm exception proposal instructions.
// Test try blocks with no handlers.
assertEq(
wasmEvalText(
`(module
(func (export "f" ) (result i32)
try (result i32) (i32.const 0) end))`
).exports.f(),
0
);
assertEq(
wasmEvalText(
`(module
(func (export "f" ) (result i32)
try (result i32) (i32.const 0) (br 0) (i32.const 1) end))`
).exports.f(),
0
);
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
try (result i32)
(throw $exn)
(i32.const 1)
end
drop
(i32.const 2)
catch $exn
(i32.const 0)
end))`
).exports.f(),
0
);
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
try (result i32)
try
try
(throw $exn)
end
end
(i32.const 1)
end
drop
(i32.const 2)
catch $exn
(i32.const 0)
end))`
).exports.f(),
0
);
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
try (result i32)
try
try
(throw $exn)
end
catch_all
rethrow 0
end
(i32.const 1)
end
drop
(i32.const 2)
catch $exn
(i32.const 0)
end))`
).exports.f(),
0
);
// Test trivial try-catch with empty bodies.
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try nop catch $exn end
(i32.const 0)))`
).exports.f(),
0
);
assertEq(
wasmEvalText(
`(module
(func (export "f" ) (result i32)
try nop catch_all end
(i32.const 0)))`
).exports.f(),
0
);
// Test try block with no throws
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
(i32.const 0)
catch $exn
(i32.const 1)
end))`
).exports.f(),
0
);
// Ensure catch block is really not run when no throw occurs.
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32) (local i32)
try
(local.set 0 (i32.const 42))
catch $exn
(local.set 0 (i32.const 99))
end
(local.get 0)))`
).exports.f(),
42
);
// Simple uses of throw.
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
(i32.const 42)
(throw $exn)
catch $exn
drop
(i32.const 1)
end))`
).exports.f(),
1
);
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn (type 0))
(func $foo (param i32) (result i32)
(local.get 0) (throw $exn))
(func (export "f" ) (result i32)
try (result i32)
(i32.const 42)
(call $foo)
catch $exn
drop
(i32.const 1)
end))`
).exports.f(),
1
);
// Simple uses of throw of some Wasm vectortype values (Simd128).
if (wasmSimdEnabled()) {
assertEq(
wasmEvalText(
`(module
(type (func (param v128 v128 v128 v128)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
(v128.const i32x4 42 41 40 39)
(v128.const f32x4 4.2 4.1 0.40 3.9)
(v128.const i64x2 42 41)
(v128.const f64x2 4.2 4.1)
(throw $exn)
catch $exn
drop drop drop
(i64x2.all_true)
end))`
).exports.f(),
1
);
assertEq(
wasmEvalText(
`(module
(type (func (param v128 v128 v128 v128)))
(tag $exn (type 0))
(func $foo (param v128 v128 v128 v128) (result i32)
(throw $exn (local.get 0)
(local.get 1)
(local.get 2)
(local.get 3)))
(func (export "f" ) (result i32)
try (result i32)
(v128.const i32x4 42 41 40 39)
(v128.const f32x4 4.2 4.1 0.40 3.9)
(v128.const i64x2 42 41)
(v128.const f64x2 4.2 4.1)
(call $foo)
catch $exn
drop drop drop
(i64x2.all_true)
end))`
).exports.f(),
1
);
{
let imports =
wasmEvalText(
`(module
(tag $exn (export "exn" ) (param v128))
(func (export "throws" ) (param v128) (result v128)
(throw $exn (local.get 0))
(v128.const i32x4 9 10 11 12)))`).exports;
let mod =
`(module
(import "m" "exn" (tag $exn (param v128)))
(import "m" "throws" (func $throws (param v128) (result v128)))
(func (export "f" ) (result i32) (local v128)
(v128.const i32x4 1 2 3 4)
(local.tee 0)
try (param v128) (result v128)
(call $throws )
catch $exn
catch_all (v128.const i32x4 5 6 7 8)
end
(local.get 0)
(i32x4.eq)
(i32x4.all_true)))`;
assertEq(wasmEvalText(mod, { m : imports }).exports.f(), 1);
}
}
// Further nested call frames should be ok.
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn (type 0))
(func $foo (param i32) (result i32)
(local.get 0) (call $bar))
(func $bar (param i32) (result i32)
(local.get 0) (call $quux))
(func $quux (param i32) (result i32)
(local.get 0) (throw $exn))
(func (export "f" ) (result i32)
try (result i32)
(i32.const 42)
(call $foo)
catch $exn
drop
(i32.const 1)
end))`
).exports.f(),
1
);
// Basic throwing from loop.
assertEq(
wasmEvalText(
`(module
(tag $exn)
;; For the purpose of this test, the params below should be increasing.
(func (export "f" ) (param $depth_to_throw_exn i32)
(param $maximum_loop_iterations i32)
(result i32)
(local $loop_counter i32)
;; The loop is counting down.
(local.get $maximum_loop_iterations)
(local.set $loop_counter)
(block $catch
(loop $loop
(if (i32.eqz (local.get $loop_counter))
(then
(return (i32.const 440)))
(else
try
(if (i32.eq (local.get $depth_to_throw_exn)
(local.get $loop_counter))
(then
(throw $exn))
(else
(local.set $loop_counter
(i32.sub (local.get $loop_counter)
(i32.const 1)))))
catch $exn (br $catch )
catch_all
end))
(br $loop))
(return (i32.const 10001)))
(i32.const 10000)))`
).exports.f(2, 4),
10000
);
// Ensure conditional throw works.
let conditional = wasmEvalText(
`(module
(type (func (param)))
(tag $exn (type 0))
(func (export "f" ) (param i32) (result i32)
try (result i32)
(local.get 0)
if (result i32)
(throw $exn)
else
(i32.const 42)
end
catch $exn
(i32.const 99)
end))`
).exports.f;
assertEq(conditional(0), 42);
assertEq(conditional(1), 99);
// Ensure multiple & nested try-catch blocks work.
assertEq(
wasmEvalText(
`(module
(type (func (param)))
(tag $exn (type 0))
(func $foo (throw $exn))
(func (export "f" ) (result i32) (local i32)
try
nop
catch $exn
(local.set 0 (i32.const 99))
end
try
(call $foo)
catch $exn
(local.set 0 (i32.const 42))
end
(local.get 0)))`
).exports.f(),
42
);
assertEq(
wasmEvalText(
`(module
(type (func (param)))
(tag $exn (type 0))
(func (export "f" ) (result i32) (local i32)
try
try
try
(throw $exn)
catch $exn
(local.set 0 (i32.const 42))
end
catch $exn
(local.set 0 (i32.const 97))
end
catch $exn
(local.set 0 (i32.const 98))
end
(local.get 0)))`
).exports.f(),
42
);
assertEq(
wasmEvalText(
`(module
(tag $exn)
(func (export "f" ) (result i32)
try
throw $exn
catch $exn
try
(throw $exn)
catch $exn
end
end
(i32.const 27)))`
).exports.f(),
27
);
{
let nested_throw_in_block_and_in_catch =
wasmEvalText(
`(module
(tag $exn)
(func $throw
(throw $exn))
(func (export "f" ) (param $arg i32) (result i32)
(block (result i32)
try (result i32)
(call $throw )
(unreachable)
catch $exn
(if (result i32)
(local.get $arg)
(then
try (result i32)
(call $throw )
(unreachable)
catch $exn
(i32.const 27)
end)
(else
(i32.const 11))
)
end
)
))`
).exports.f;
assertEq(nested_throw_in_block_and_in_catch(1), 27);
assertEq(nested_throw_in_block_and_in_catch(0), 11);
}
assertEq(
wasmEvalText(
`(module
(tag $thrownExn)
(tag $notThrownExn)
(func (export "f" ) (result i32)
try (result i32)
try (result i32)
(throw $thrownExn)
catch $notThrownExn
(i32.const 19)
catch $thrownExn
(i32.const 20)
catch_all
(i32.const 21)
end
end))`
).exports.f(),
20
);
// Test that uncaught exceptions get propagated.
assertEq(
wasmEvalText(
`(module
(tag $thrownExn)
(tag $notThrownExn)
(func (export "f" ) (result i32) (local i32)
try
try
try
(throw $thrownExn)
catch $notThrownExn
(local.set 0
(i32.or (local.get 0)
(i32.const 1)))
end
catch $notThrownExn
(local.set 0
(i32.or (local.get 0)
(i32.const 2)))
end
catch $thrownExn
(local.set 0
(i32.or (local.get 0)
(i32.const 4)))
end
(local.get 0)))`
).exports.f(),
4
);
// Test tag dispatch for catches.
assertEq(
wasmEvalText(
`(module
(type (func (param)))
(tag $exn1 (type 0))
(tag $exn2 (type 0))
(tag $exn3 (type 0))
(func (export "f" ) (result i32)
try (result i32)
throw $exn1
catch $exn1
i32.const 1
catch $exn2
i32.const 2
catch $exn3
i32.const 3
end))`
).exports.f(),
1
);
assertEq(
wasmEvalText(
`(module
(type (func (param)))
(tag $exn1 (type 0))
(tag $exn2 (type 0))
(tag $exn3 (type 0))
(func (export "f" ) (result i32)
try (result i32)
throw $exn2
catch $exn1
i32.const 1
catch $exn2
i32.const 2
catch $exn3
i32.const 3
end))`
).exports.f(),
2
);
assertEq(
wasmEvalText(
`(module
(type (func (param)))
(tag $exn1 (type 0))
(tag $exn2 (type 0))
(tag $exn3 (type 0))
(func (export "f" ) (result i32)
try (result i32)
throw $exn3
catch $exn1
i32.const 1
catch $exn2
i32.const 2
catch $exn3
i32.const 3
end))`
).exports.f(),
3
);
assertEq(
wasmEvalText(
`(module
(type (func (param)))
(tag $exn1 (type 0))
(tag $exn2 (type 0))
(tag $exn3 (type 0))
(tag $exn4 (type 0))
(func (export "f" ) (result i32)
try (result i32)
try (result i32)
throw $exn4
catch $exn1
i32.const 1
catch $exn2
i32.const 2
catch $exn3
i32.const 3
end
catch $exn4
i32.const 4
end))`
).exports.f(),
4
);
// Test usage of br before a throw.
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try $l (result i32)
(i32.const 2)
(br $l)
(throw $exn)
catch $exn
drop
(i32.const 1)
end))`
).exports.f(),
2
);
assertEq(
wasmEvalText(
`(module
(type (func (param)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try $l (result i32)
(throw $exn)
catch $exn
(i32.const 2)
(br $l)
rethrow 0
end))`
).exports.f(),
2
);
assertEq(
wasmEvalText(
`(module
(type (func (param)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try $l (result i32)
(throw $exn)
catch_all
(i32.const 2)
(br $l)
rethrow 0
end))`
).exports.f(),
2
);
// Test br branching out of a catch block.
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
block $l (result i32)
block (result i32)
try (result i32)
(i32.const 42)
(throw $exn)
catch $exn
br $l
(i32.const 99)
end
end
end))`
).exports.f(),
42
);
// Test dead catch block.
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32)
i32.const 0
return
try nop catch $exn end))`
).exports.f(),
0
);
assertEq(
wasmEvalText(
`(module
(func (export "f" ) (result i32)
i32.const 0
return
try nop catch_all end))`
).exports.f(),
0
);
// Test catch with exception values pushed to stack.
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(type (func (param i32)))
(type (func (param i64)))
(tag $exn (type 0))
(tag $foo (type 1))
(tag $bar (type 2))
(func (export "f" ) (result i32)
try $l (result i32)
(i32.const 42)
(throw $exn)
catch $exn
catch_all
(i32.const 99)
end))`
).exports.f(),
42
);
// Throw an exception carrying more than one value.
assertEq(
wasmEvalText(
`(module
(type (func (param i32 i64 f32 f64)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try $l (result i32 i64 f32 f64)
(i32.const 42)
(i64.const 84)
(f32.const 42.2)
(f64.const 84.4)
(throw $exn)
catch $exn
catch_all
(i32.const 99)
(i64.const 999)
(f32.const 99.9)
(f64.const 999.9)
end
drop drop drop))`
).exports.f(),
42
);
// This should also work inside nested frames.
assertEq(
wasmEvalText(
`(module
(type (func (param i32 i64 f32 f64)))
(tag $exn (type 0))
(func $foo (param i32 i64 f32 f64) (result i32 i64 f32 f64)
(local.get 0)
(local.get 1)
(local.get 2)
(local.get 3)
(throw $exn))
(func (export "f" ) (result i32)
try $l (result i32 i64 f32 f64)
(i32.const 42)
(i64.const 84)
(f32.const 42.2)
(f64.const 84.4)
(call $foo)
catch $exn
catch_all
(i32.const 99)
(i64.const 999)
(f32.const 99.9)
(f64.const 999.9)
end
drop drop drop))`
).exports.f(),
42
);
// Multiple tagged catch in succession.
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn1 (type 0))
(tag $exn2 (type 0))
(func (export "f" ) (result i32)
try (result i32)
(i32.const 42)
(throw $exn2)
catch $exn1
catch $exn2
catch_all
(i32.const 99)
end))`
).exports.f(),
42
);
assertEq(
wasmEvalText(
`(module
(tag $exn0)
(tag $exn1)
(tag $exn2)
(tag $exn3)
(tag $exn4)
(tag $exn5)
(func (export "f" ) (result i32)
try (result i32)
(throw $exn4)
catch $exn5 (i32.const 5)
catch $exn2 (i32.const 2)
catch $exn4 (i32.const 4) ;; Caught here.
catch $exn4 (i32.const 44)
end
))`
).exports.f(),
4
);
// Try catch with block parameters.
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32)
(i32.const 42)
try (param i32) (result i32)
nop
catch $exn
(i32.const 99)
end))`
).exports.f(),
42
);
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
(i32.const 42)
try $l (param i32) (result i32)
(throw $exn)
catch $exn
catch_all
(i32.const 99)
end))`
).exports.f(),
42
);
// Test the catch_all case.
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn1 (type 0))
(tag $exn2 (type 0))
(func (export "f" ) (result i32)
try $l (result i32)
(i32.const 42)
(throw $exn2)
catch $exn1
catch_all
(i32.const 99)
end))`
).exports.f(),
99
);
assertEq(
wasmEvalText(
`(module
(tag $exn (param i32))
(func (export "f" ) (result i32)
try (result i32)
try (result i32)
(i32.const 42)
(throw $exn)
catch_all
(i32.const 99)
end
catch $exn
end))`
).exports.f(),
99
);
// Test foreign exception catch.
assertEq(
wasmEvalText(
`(module
(type (func))
(import "m" "foreign" (func $foreign))
(tag $exn (type 0))
(func (export "f" ) (result i32) (local i32)
try $l
(call $foreign)
catch $exn
catch_all
(local.set 0 (i32.const 42))
end
(local.get 0)))`,
{
m: {
foreign() {
throw 5;
},
},
}
).exports.f(),
42
);
// Exception handlers should not catch traps.
assertErrorMessage(
() =>
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32) (local i32)
try $l
unreachable
catch $exn
(local.set 0 (i32.const 98))
catch_all
(local.set 0 (i32.const 99))
end
(local.get 0)))`
).exports.f(),
WebAssembly.RuntimeError,
"unreachable executed"
);
// Ensure that a RuntimeError created by the user is not filtered out
// as a trap emitted by the runtime (i.e., the filtering predicate is not
// observable from JS).
assertEq(
wasmEvalText(
`(module
(import "m" "foreign" (func $foreign))
(func (export "f" ) (result i32)
try (result i32)
(call $foreign)
(i32.const 99)
catch_all
(i32.const 42)
end))`,
{
m: {
foreign() {
throw new WebAssembly.RuntimeError();
},
},
}
).exports.f(),
42
);
// Test uncatchable JS exceptions (OOM & stack overflow).
{
let f = wasmEvalText(
`(module
(import "m" "foreign" (func $foreign))
(func (export "f" ) (result)
try
(call $foreign)
catch_all
end))`,
{
m: {
foreign() {
throwOutOfMemory();
},
},
}
).exports.f;
var thrownVal;
try {
f();
} catch (exn) {
thrownVal = exn;
}
assertEq(thrownVal, "out of memory" );
}
assertErrorMessage(
() =>
wasmEvalText(
`(module
(import "m" "foreign" (func $foreign))
(func (export "f" )
try
(call $foreign)
catch_all
end))`,
{
m: {
foreign: function foreign() {
foreign();
},
},
}
).exports.f(),
Error,
"too much recursion"
);
// Test all implemented instructions in a single module.
{
let divFunctypeInline =
`(param $numerator i32) (param $denominator i32) (result i32)`;
let safediv = wasmEvalText(
`(module
(tag $divexn (param i32 i32))
(tag $notThrownExn (param i32))
(func $throwingdiv ${divFunctypeInline}
(local.get $numerator)
(local.get $denominator)
(if (param i32 i32) (result i32)
(i32.eqz (local.get $denominator))
(then
try (param i32 i32)
(throw $divexn)
delegate 0
(i32.const 9))
(else
i32.div_u)))
(func $safediv (export "safediv" ) ${divFunctypeInline}
(local.get $numerator)
(local.get $denominator)
try (param i32 i32) (result i32)
(call $throwingdiv)
catch $notThrownExn
catch $divexn
i32.add
catch_all
(i32.const 44)
end
))`
).exports.safediv;
assertEq(safediv(6, 3), 2);
assertEq(safediv(6, 0), 6);
}
// Test simple rethrow.
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
try
throw $exn
catch $exn
rethrow 0
end
i32.const 1
catch $exn
i32.const 27
end))`
).exports.f(),
27
);
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
try
throw $exn
catch_all
rethrow 0
end
i32.const 1
catch $exn
i32.const 27
end))`
).exports.f(),
27
);
// Test rethrows in nested blocks.
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
try
throw $exn
catch $exn
block
rethrow 1
end
end
i32.const 1
catch $exn
i32.const 27
end))`
).exports.f(),
27
);
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
try
throw $exn
catch_all
block
rethrow 1
end
end
i32.const 1
catch $exn
i32.const 27
end))`
).exports.f(),
27
);
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn1 (type 0))
(tag $exn2 (type 0))
(func (export "f" ) (result i32)
try (result i32)
try
throw $exn1
catch $exn1
try
throw $exn2
catch $exn2
rethrow 1
end
end
i32.const 0
catch $exn1
i32.const 1
catch $exn2
i32.const 2
end))`
).exports.f(),
1
);
assertEq(
wasmEvalText(
`(module
(type (func))
(tag $exn1 (type 0))
(tag $exn2 (type 0))
(func (export "f" ) (result i32)
try (result i32)
try
throw $exn1
catch $exn1
try
throw $exn2
catch_all
rethrow 1
end
end
i32.const 0
catch $exn1
i32.const 1
catch $exn2
i32.const 2
end))`
).exports.f(),
1
);
// Test that rethrow makes the rest of the block dead code.
assertEq(
wasmEvalText(
`(module
(tag (param i32))
(func (export "f" ) (result i32)
try (result i32)
(i32.const 1)
catch 0
(rethrow 0)
(i32.const 2)
end
))`
).exports.f(),
1
);
assertEq(
wasmEvalText(
`(module
(tag (param i32))
(func (export "f" ) (result i32)
try (result i32)
try
(i32.const 13)
(throw 0)
catch 0
(rethrow 0)
end
(unreachable)
catch 0
end
))`
).exports.f(),
13
);
assertEq(
wasmEvalText(
`(module
(tag)
(func (export "f" ) (result i32)
try (result i32)
try
(throw 0)
catch 0
(i32.const 4)
(rethrow 0)
end
(unreachable)
catch 0
(i32.const 13)
end
))`
).exports.f(),
13
);
assertEq(
wasmEvalText(
`(module
(tag (param i32))
(func (export "f" ) (result i32)
try (result i32)
try
(i32.const 13)
(throw 0)
catch 0
(i32.const 111)
(rethrow 0)
end
(i32.const 222)
catch 0
end
))`
).exports.f(),
13
);
// Test try-delegate blocks.
// Dead delegate to caller
assertEq(
wasmEvalText(
`(module
(tag $exn (param))
(func (export "f" ) (result i32)
i32.const 1
br 0
try
throw $exn
delegate 0))`
).exports.f(),
1
);
// Nested try-delegate.
assertEq(
wasmEvalText(
`(module
(tag $exn (param))
(func (export "f" ) (result i32)
try (result i32)
try
try
throw $exn
delegate 0
end
i32.const 0
catch $exn
i32.const 1
end))`
).exports.f(),
1
);
// Non-throwing and breaking try-delegate.
assertEq(
wasmEvalText(
`(module
(tag $exn (param))
(func (export "f" ) (result i32)
try (result i32)
i32.const 1
br 0
delegate 0))`
).exports.f(),
1
);
assertEq(
wasmEvalText(
`(module
(tag $exn (param))
(func (export "f" ) (result i32)
try (result i32)
i32.const 1
return
delegate 0))`
).exports.f(),
1
);
// More nested try-delegate.
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
try
i32.const 42
throw $exn
delegate 0
i32.const 0
catch $exn
i32.const 1
i32.add
end))`
).exports.f(),
43
);
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
try (result i32)
try
i32.const 42
throw $exn
delegate 1
i32.const 0
catch $exn
i32.const 1
i32.add
end
catch $exn
i32.const 2
i32.add
end))`
).exports.f(),
44
);
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
try (result i32)
try (result i32)
try
i32.const 42
throw $exn
delegate 1
i32.const 0
catch $exn
i32.const 1
i32.add
end
delegate 0
catch $exn
i32.const 2
i32.add
end))`
).exports.f(),
44
);
assertEq(
wasmEvalText(
`(module
(tag $exn (param))
(func $g (param i32) (result i32) (i32.const 42))
(func (export "f" ) (result i32)
try (result i32)
try $t
block (result i32)
(i32.const 4)
(call $g)
try
throw $exn
delegate $t
end
drop
end
i32.const 0
catch_all
i32.const 1
end))`
).exports.f(),
1
);
// Test delegation to function body and blocks.
// Non-throwing.
assertEq(
wasmEvalText(
`(module
(tag $exn (param))
(func (export "f" ) (result i32)
try (result i32)
i32.const 1
delegate 0))`
).exports.f(),
1
);
// Block target.
assertEq(
wasmEvalText(
`(module
(tag $exn (param i32))
(func (export "f" ) (result i32)
try (result i32)
block
try
i32.const 1
throw $exn
delegate 0
end
i32.const 0
catch $exn
end))`
).exports.f(),
1
);
// Catch target.
assertEq(
wasmEvalText(
`(module
(tag $exn (param))
(func (export "f" ) (result i32)
try (result i32)
try
throw $exn
catch $exn
try
throw $exn
delegate 0
end
i32.const 0
catch_all
i32.const 1
end))`
).exports.f(),
1
);
// Target function body.
assertEq(
wasmEvalText(
`(module
(type (func (param i32)))
(tag $exn (type 0))
(func (export "f" ) (result i32)
try (result i32)
call $g
catch $exn
end)
(func $g (result i32)
try (result i32)
try
i32.const 42
throw $exn
delegate 1
i32.const 0
catch $exn
i32.const 1
i32.add
end))`
).exports.f(),
42
);
// Try-delegate from inside a loop.
assertEq(
wasmEvalText(
`(module
(tag $exn)
;; For the purpose of this test, the params below should be increasing.
(func (export "f" ) (param $depth_to_throw_exn i32)
(param $maximum_loop_iterations i32)
(result i32)
(local $loop_countdown i32)
;; Counts how many times the loop was started.
(local $loop_verifier i32)
;; The loop is counting down.
(local.get $maximum_loop_iterations)
(local.set $loop_countdown)
try $catch_exn (result i32)
try
(loop $loop
;; Counts how many times the loop was started.
(local.set $loop_verifier
(i32.add (i32.const 1)
(local.get $loop_verifier)))
(if (i32.eqz (local.get $loop_countdown))
(then (return (i32.const 440)))
(else
try $rethrow_label
(if (i32.eq (local.get $depth_to_throw_exn)
(local.get $loop_countdown))
(then (throw $exn))
(else
(local.set $loop_countdown
(i32.sub (local.get $loop_countdown)
(i32.const 1)))))
catch $exn try
(rethrow $rethrow_label)
delegate $catch_exn
end
))
(br $loop))
catch_all unreachable
end
(i32.const 2000)
catch_all (i32.const 10000)
end
(i32.add (local.get $loop_verifier))))`
).exports.f(3, 5),
10003
);
Messung V0.5 C=96 H=95 G=95
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland