blob: 74d8e7dfb5ec58c7c6a3ff7be7f8b88dc111465a [file] [log] [blame]
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --experimental-wasm-eh
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
// The following method doesn't attempt to catch an raised exception.
var test_throw = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_v);
builder.addFunction("throw_if_param_not_zero", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprI32Const, 0,
kExprI32Ne,
kExprIf, kWasmStmt,
kExprThrow, 0,
kExprEnd,
kExprI32Const, 1
]).exportFunc();
return builder.instantiate();
})();
// Check the test_throw exists.
assertFalse(test_throw === undefined);
assertFalse(test_throw === null);
assertFalse(test_throw === 0);
assertEquals("object", typeof test_throw.exports);
assertEquals("function", typeof test_throw.exports.throw_if_param_not_zero);
// Test expected behavior of throws
assertEquals(1, test_throw.exports.throw_if_param_not_zero(0));
assertWasmThrows(0, [], function() { test_throw.exports.throw_if_param_not_zero(10) });
assertWasmThrows(0, [], function() { test_throw.exports.throw_if_param_not_zero(-1) });
// Now that we know throwing works, we test catching the exceptions we raise.
var test_catch = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_v);
builder.addFunction("simple_throw_catch_to_0_1", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprI32Eqz,
kExprIf, kWasmStmt,
kExprThrow, 0,
kExprEnd,
kExprI32Const, 1,
kExprCatch, 0,
kExprI32Const, 0,
kExprEnd
]).exportFunc();
return builder.instantiate();
})();
// Check the test_catch exists.
assertFalse(test_catch === undefined);
assertFalse(test_catch === null);
assertFalse(test_catch === 0);
assertEquals("object", typeof test_catch.exports);
assertEquals("function", typeof test_catch.exports.simple_throw_catch_to_0_1);
// Test expected behavior of simple catch.
assertEquals(0, test_catch.exports.simple_throw_catch_to_0_1(0));
assertEquals(1, test_catch.exports.simple_throw_catch_to_0_1(1));
// Test that we can distinguish which exception was thrown.
var test_catch_2 = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_v);
builder.addException(kSig_v_v);
builder.addException(kSig_v_v);
builder.addFunction("catch_different_exceptions", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprI32Eqz,
kExprIf, kWasmStmt,
kExprThrow, 0,
kExprElse,
kExprGetLocal, 0,
kExprI32Const, 1,
kExprI32Eq,
kExprIf, kWasmStmt,
kExprThrow, 1,
kExprElse,
kExprThrow, 2,
kExprEnd,
kExprEnd,
kExprI32Const, 2,
kExprCatch, 0,
kExprI32Const, 3,
kExprEnd,
kExprCatch, 1,
kExprI32Const, 4,
kExprEnd
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_catch_2 === undefined);
assertFalse(test_catch_2 === null);
assertFalse(test_catch_2 === 0);
assertEquals("object", typeof test_catch_2.exports);
assertEquals("function", typeof test_catch_2.exports.catch_different_exceptions);
assertEquals(3, test_catch_2.exports.catch_different_exceptions(0));
assertEquals(4, test_catch_2.exports.catch_different_exceptions(1));
assertWasmThrows(2, [], function() { test_catch_2.exports.catch_different_exceptions(2) });
// Test throwing an exception with multiple values.
var test_throw_1_2 = (function() {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_ii);
builder.addFunction("throw_1_2", kSig_v_v)
.addBody([
kExprI32Const, 1,
kExprI32Const, 2,
kExprThrow, 0,
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_1_2 === undefined);
assertFalse(test_throw_1_2 === null);
assertFalse(test_throw_1_2 === 0);
assertEquals("object", typeof test_throw_1_2.exports);
assertEquals("function", typeof test_throw_1_2.exports.throw_1_2);
assertWasmThrows(0, [0, 1, 0, 2], function() { test_throw_1_2.exports.throw_1_2(); });
// Test throwing/catching the i32 parameter value.
var test_throw_catch_param_i = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_i);
builder.addFunction("throw_catch_param", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprThrow, 0,
kExprI32Const, 2,
kExprCatch, 0,
kExprReturn,
kExprEnd,
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_catch_param_i === undefined);
assertFalse(test_throw_catch_param_i === null);
assertFalse(test_throw_catch_param_i === 0);
assertEquals("object", typeof test_throw_catch_param_i.exports);
assertEquals("function",
typeof test_throw_catch_param_i.exports.throw_catch_param);
assertEquals(0, test_throw_catch_param_i.exports.throw_catch_param(0));
assertEquals(1, test_throw_catch_param_i.exports.throw_catch_param(1));
assertEquals(10, test_throw_catch_param_i.exports.throw_catch_param(10));
// Test the encoding of a thrown exception with an integer exception.
var test_throw_param_i = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_i);
builder.addFunction("throw_param", kSig_v_i)
.addBody([
kExprGetLocal, 0,
kExprThrow, 0,
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_param_i === undefined);
assertFalse(test_throw_param_i === null);
assertFalse(test_throw_param_i === 0);
assertEquals("object", typeof test_throw_param_i.exports);
assertEquals("function",
typeof test_throw_param_i.exports.throw_param);
assertWasmThrows(0, [0, 5], function() { test_throw_param_i.exports.throw_param(5); });
assertWasmThrows(0, [6, 31026],
function() { test_throw_param_i.exports.throw_param(424242); });
// Test throwing/catching the f32 parameter value.
var test_throw_catch_param_f = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_f);
builder.addFunction("throw_catch_param", kSig_f_f)
.addBody([
kExprTry, kWasmF32,
kExprGetLocal, 0,
kExprThrow, 0,
kExprF32Const, 0, 0, 0, 0,
kExprCatch, 0,
kExprReturn,
kExprEnd,
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_catch_param_f === undefined);
assertFalse(test_throw_catch_param_f === null);
assertFalse(test_throw_catch_param_f === 0);
assertEquals("object", typeof test_throw_catch_param_f.exports);
assertEquals("function",
typeof test_throw_catch_param_f.exports.throw_catch_param);
assertEquals(5.0, test_throw_catch_param_f.exports.throw_catch_param(5.0));
assertEquals(10.5, test_throw_catch_param_f.exports.throw_catch_param(10.5));
// Test the encoding of a thrown exception with a float value.
var test_throw_param_f = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_f);
builder.addFunction("throw_param", kSig_v_f)
.addBody([
kExprGetLocal, 0,
kExprThrow, 0,
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_param_f === undefined);
assertFalse(test_throw_param_f === null);
assertFalse(test_throw_param_f === 0);
assertEquals("object", typeof test_throw_param_f.exports);
assertEquals("function",
typeof test_throw_param_f.exports.throw_param);
assertWasmThrows(0, [16544, 0],
function() { test_throw_param_f.exports.throw_param(5.0); });
assertWasmThrows(0, [16680, 0],
function() { test_throw_param_f.exports.throw_param(10.5); });
// Test throwing/catching an I64 value
var test_throw_catch_param_l = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_l);
builder.addFunction("throw_catch_param", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprI64UConvertI32,
kExprSetLocal, 1,
kExprTry, kWasmI32,
kExprGetLocal, 1,
kExprThrow, 0,
kExprI32Const, 2,
kExprCatch, 0,
kExprGetLocal, 1,
kExprI64Eq,
kExprIf, kWasmI32,
kExprI32Const, 1,
kExprElse,
kExprI32Const, 0,
kExprEnd,
// TODO(kschimpf): Why is this return necessary?
kExprReturn,
kExprEnd,
]).addLocals({i64_count: 1}).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_catch_param_l === undefined);
assertFalse(test_throw_catch_param_l === null);
assertFalse(test_throw_catch_param_l === 0);
assertEquals("object", typeof test_throw_catch_param_l.exports);
assertEquals("function",
typeof test_throw_catch_param_l.exports.throw_catch_param);
assertEquals(1, test_throw_catch_param_l.exports.throw_catch_param(5));
assertEquals(1, test_throw_catch_param_l.exports.throw_catch_param(0));
assertEquals(1, test_throw_catch_param_l.exports.throw_catch_param(-1));
// Test the encoding of a thrown exception with an I64 value.
var test_throw_param_l = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_l);
builder.addFunction("throw_param", kSig_v_ii)
.addBody([
kExprGetLocal, 0,
kExprI64UConvertI32,
kExprI64Const, 32,
kExprI64Shl,
kExprGetLocal, 1,
kExprI64UConvertI32,
kExprI64Ior,
kExprThrow, 0
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_param_l === undefined);
assertFalse(test_throw_param_l === null);
assertFalse(test_throw_param_l === 0);
assertEquals("object", typeof test_throw_param_l.exports);
assertEquals("function",
typeof test_throw_param_l.exports.throw_param);
assertWasmThrows(0, [0, 10, 0, 5],
function() { test_throw_param_l.exports.throw_param(10, 5); });
assertWasmThrows(0, [65535, 65535, 0, 13],
function() { test_throw_param_l.exports.throw_param(-1, 13); });
// Test throwing/catching the F64 parameter value
var test_throw_catch_param_d = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_d);
builder.addFunction("throw_catch_param", kSig_d_d)
.addBody([
kExprTry, kWasmF64,
kExprGetLocal, 0,
kExprThrow, 0,
kExprF64Const, 0, 0, 0, 0, 0, 0, 0, 0,
kExprCatch, 0,
kExprReturn,
kExprEnd,
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_catch_param_d === undefined);
assertFalse(test_throw_catch_param_d === null);
assertFalse(test_throw_catch_param_d === 0);
assertEquals("object", typeof test_throw_catch_param_d.exports);
assertEquals("function",
typeof test_throw_catch_param_d.exports.throw_catch_param);
assertEquals(5.0, test_throw_catch_param_d.exports.throw_catch_param(5.0));
assertEquals(10.5, test_throw_catch_param_d.exports.throw_catch_param(10.5));
// Test the encoding of a thrown exception with an f64 value.
var test_throw_param_d = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_d);
builder.addFunction("throw_param", kSig_v_f)
.addBody([
kExprGetLocal, 0,
kExprF64ConvertF32,
kExprThrow, 0
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_param_d === undefined);
assertFalse(test_throw_param_d === null);
assertFalse(test_throw_param_d === 0);
assertEquals("object", typeof test_throw_param_d.exports);
assertEquals("function",
typeof test_throw_param_d.exports.throw_param);
assertWasmThrows(0, [16404, 0, 0, 0],
function() { test_throw_param_d.exports.throw_param(5.0); });
assertWasmThrows(0, [16739, 4816, 0, 0],
function() { test_throw_param_d.exports.throw_param(10000000.5); });
/* TODO(kschimpf) Convert these tests to work for the proposed exceptions.
// The following methods do not attempt to catch the exception they raise.
var test_throw = (function () {
var builder = new WasmModuleBuilder();
builder.addFunction("throw_expr_with_params", kSig_v_ddi)
.addBody([
// p2 * (p0 + min(p0, p1))|0 - 20
kExprGetLocal, 2,
kExprGetLocal, 0,
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprF64Min,
kExprF64Add,
kExprI32SConvertF64,
kExprI32Mul,
kExprI32Const, 20,
kExprI32Sub,
kExprThrow,
])
.exportFunc()
return builder.instantiate();
})();
// Check the test_throw exists.
assertFalse(test_throw === undefined);
assertFalse(test_throw === null);
assertFalse(test_throw === 0);
assertEquals("object", typeof test_throw.exports);
assertEquals("function", typeof test_throw.exports.throw_expr_with_params);
assertEquals(1, test_throw.exports.throw_param_if_not_zero(0));
assertWasmThrows(
-8, function() { test_throw.exports.throw_expr_with_params(1.5, 2.5, 4); });
assertWasmThrows(
12, function() { test_throw.exports.throw_expr_with_params(5.7, 2.5, 4); });
// Now that we know throwing works, we test catching the exceptions we raise.
var test_catch = (function () {
var builder = new WasmModuleBuilder();
// Helper function for throwing from js. It is imported by the Wasm module
// as throw_i.
function throw_value(value) {
throw value;
}
var sig_index = builder.addType(kSig_v_i);
var kJSThrowI = builder.addImport("", "throw_i", sig_index);
// Helper function that throws a string. Wasm should not catch it.
function throw_string() {
throw "use wasm;";
}
sig_index = builder.addType(kSig_v_v);
var kJSThrowString = builder.addImport("", "throw_string", sig_index);
// Helper function that throws undefined. Wasm should not catch it.
function throw_undefined() {
throw undefined;
}
var kJSThrowUndefined = builder.addImport("", "throw_undefined", sig_index);
// Helper function that throws an fp. Wasm should not catch it.
function throw_fp() {
throw 10.5;
}
var kJSThrowFP = builder.addImport("", "throw_fp", sig_index);
// Helper function that throws a large number. Wasm should not catch it.
function throw_large() {
throw 1e+28;
}
var kJSThrowLarge = builder.addImport("", "throw_large", sig_index);
// Helper function for throwing from WebAssembly.
var kWasmThrowFunction =
builder.addFunction("throw", kSig_v_i)
.addBody([
kExprGetLocal, 0,
kExprThrow
])
.index;
// Scenario 1: Throw and catch appear on the same function. This should
// happen in case of inlining, for example.
builder.addFunction("same_scope", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprI32Const, 0,
kExprI32Ne,
kExprIf, kWasmStmt,
kExprGetLocal, 0,
kExprThrow,
kExprUnreachable,
kExprEnd,
kExprI32Const, 63,
kExprCatch, 1,
kExprGetLocal, 1,
kExprEnd
])
.addLocals({i32_count: 1})
.exportFunc()
.index;
builder.addFunction("same_scope_ignore", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprThrow,
kExprUnreachable,
kExprCatch, 1,
kExprGetLocal, 0,
kExprEnd,
])
.addLocals({i32_count: 1})
.exportFunc();
builder.addFunction("same_scope_multiple", kSig_i_i)
// path = 0;
//
// try {
// try {
// try {
// if (p == 1)
// throw 1;
// path |= 2
// } catch (v) {
// path |= v | 4;
// throw path;
// }
// if (p == 2)
// throw path|8;
// path |= 16;
// } catch (v) {
// path |= v | 32;
// throw path;
// }
// if (p == 3)
// throw path|64;
// path |= 128
// } catch (v) {
// path |= v | 256;
// }
//
// return path;
//
// p == 1 -> path == 293
// p == 2 -> path == 298
// p == 3 -> path == 338
// else -> path == 146
.addBody([
kExprTry, kWasmI32,
kExprTry, kWasmI32,
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprI32Const, 1,
kExprI32Eq,
kExprIf, kWasmStmt,
kExprI32Const, 1,
kExprThrow,
kExprUnreachable,
kExprEnd,
kExprI32Const, 2,
kExprCatch, 1,
kExprGetLocal, 1,
kExprI32Const, 4,
kExprI32Ior,
kExprThrow,
kExprUnreachable,
kExprEnd,
kExprTeeLocal, 2,
kExprGetLocal, 0,
kExprI32Const, 2,
kExprI32Eq,
kExprIf, kWasmStmt,
kExprGetLocal, 2,
kExprI32Const, 8,
kExprI32Ior,
kExprThrow,
kExprUnreachable,
kExprEnd,
kExprI32Const, 16,
kExprI32Ior,
kExprCatch, 1,
kExprGetLocal, 1,
kExprI32Const, 32,
kExprI32Ior,
kExprThrow,
kExprUnreachable,
kExprEnd,
kExprTeeLocal, 2,
kExprGetLocal, 0,
kExprI32Const, 3,
kExprI32Eq,
kExprIf, kWasmStmt,
kExprGetLocal, 2,
kExprI32Const, / *64=* / 192, 0,
kExprI32Ior,
kExprThrow,
kExprUnreachable,
kExprEnd,
kExprI32Const, / *128=* / 128, 1,
kExprI32Ior,
kExprCatch, 1,
kExprGetLocal, 1,
kExprI32Const, / *256=* / 128, 2,
kExprI32Ior,
kExprEnd,
])
.addLocals({i32_count: 2})
.exportFunc();
// Scenario 2: Catches an exception raised from the direct callee.
var kFromDirectCallee =
builder.addFunction("from_direct_callee", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprCallFunction, kWasmThrowFunction,
kExprI32Const, / *-1=* / 127,
kExprCatch, 1,
kExprGetLocal, 1,
kExprEnd
])
.addLocals({i32_count: 1})
.exportFunc()
.index;
// Scenario 3: Catches an exception raised from an indirect callee.
var kFromIndirectCalleeHelper = kFromDirectCallee + 1;
builder.addFunction("from_indirect_callee_helper", kSig_v_ii)
.addBody([
kExprGetLocal, 0,
kExprI32Const, 0,
kExprI32GtS,
kExprIf, kWasmStmt,
kExprGetLocal, 0,
kExprI32Const, 1,
kExprI32Sub,
kExprGetLocal, 1,
kExprI32Const, 1,
kExprI32Sub,
kExprCallFunction, kFromIndirectCalleeHelper,
kExprEnd,
kExprGetLocal, 1,
kExprCallFunction, kWasmThrowFunction,
]);
builder.addFunction("from_indirect_callee", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprI32Const, 0,
kExprCallFunction, kFromIndirectCalleeHelper,
kExprI32Const, / *-1=* / 127,
kExprCatch, 1,
kExprGetLocal, 1,
kExprEnd
])
.addLocals({i32_count: 1})
.exportFunc();
// Scenario 4: Catches an exception raised in JS.
builder.addFunction("from_js", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprCallFunction, kJSThrowI,
kExprI32Const, / *-1=* / 127,
kExprCatch, 1,
kExprGetLocal, 1,
kExprEnd,
])
.addLocals({i32_count: 1})
.exportFunc();
// Scenario 5: Does not catch an exception raised in JS if it is not a
// number.
builder.addFunction("string_from_js", kSig_v_v)
.addBody([
kExprCallFunction, kJSThrowString
])
.exportFunc();
builder.addFunction("fp_from_js", kSig_v_v)
.addBody([
kExprCallFunction, kJSThrowFP
])
.exportFunc();
builder.addFunction("large_from_js", kSig_v_v)
.addBody([
kExprCallFunction, kJSThrowLarge
])
.exportFunc();
builder.addFunction("undefined_from_js", kSig_v_v)
.addBody([
kExprCallFunction, kJSThrowUndefined
])
.exportFunc();
return builder.instantiate({"": {
throw_i: throw_value,
throw_string: throw_string,
throw_fp: throw_fp,
throw_large, throw_large,
throw_undefined: throw_undefined
}});
})();
// Check the test_catch exists.
assertFalse(test_catch === undefined);
assertFalse(test_catch === null);
assertFalse(test_catch === 0);
assertEquals("object", typeof test_catch.exports);
assertEquals("function", typeof test_catch.exports.same_scope);
assertEquals("function", typeof test_catch.exports.same_scope_ignore);
assertEquals("function", typeof test_catch.exports.same_scope_multiple);
assertEquals("function", typeof test_catch.exports.from_direct_callee);
assertEquals("function", typeof test_catch.exports.from_indirect_callee);
assertEquals("function", typeof test_catch.exports.from_js);
assertEquals("function", typeof test_catch.exports.string_from_js);
assertEquals(63, test_catch.exports.same_scope(0));
assertEquals(1024, test_catch.exports.same_scope(1024));
assertEquals(-3, test_catch.exports.same_scope(-3));
assertEquals(-1, test_catch.exports.same_scope_ignore(-1));
assertEquals(1, test_catch.exports.same_scope_ignore(1));
assertEquals(0x7FFFFFFF, test_catch.exports.same_scope_ignore(0x7FFFFFFF));
assertEquals(1024, test_catch.exports.same_scope_ignore(1024));
assertEquals(-1, test_catch.exports.same_scope_ignore(-1));
assertEquals(293, test_catch.exports.same_scope_multiple(1));
assertEquals(298, test_catch.exports.same_scope_multiple(2));
assertEquals(338, test_catch.exports.same_scope_multiple(3));
assertEquals(146, test_catch.exports.same_scope_multiple(0));
assertEquals(-10024, test_catch.exports.from_direct_callee(-10024));
assertEquals(3334333, test_catch.exports.from_direct_callee(3334333));
assertEquals(-1, test_catch.exports.from_direct_callee(0xFFFFFFFF));
assertEquals(0x7FFFFFFF, test_catch.exports.from_direct_callee(0x7FFFFFFF));
assertEquals(-10, test_catch.exports.from_indirect_callee(10));
assertEquals(-77, test_catch.exports.from_indirect_callee(77));
assertEquals(10, test_catch.exports.from_js(10));
assertEquals(-10, test_catch.exports.from_js(-10));
assertThrowsEquals(test_catch.exports.string_from_js, "use wasm;");
assertThrowsEquals(test_catch.exports.large_from_js, 1e+28);
assertThrowsEquals(test_catch.exports.undefined_from_js, undefined);
*/