// 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 in Prozent C=98 H=98 G=97
¤ Dauer der Verarbeitung: 0.14 Sekunden
¤
*© Formatika GbR, Deutschland