| // Copyright 2019 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-reftypes --experimental-wasm-return-call |
| |
| load("test/mjsunit/wasm/wasm-module-builder.js"); |
| |
| (function IndirectCallToNonZeroTable() { |
| print(arguments.callee.name); |
| |
| const builder = new WasmModuleBuilder(); |
| const placeholder = builder.addTable(kWasmAnyFunc, 3).index; |
| const table1 = builder.addTable(kWasmAnyFunc, 3).index; |
| const table2 = builder.addTable(kWasmAnyFunc, 5).index; |
| const sig_index = builder.addType(kSig_i_v); |
| const other_sig = builder.addType(kSig_i_i); |
| |
| const v1 = 16; |
| const v2 = 26; |
| const v3 = 36; |
| const v4 = 46; |
| const v5 = 56; |
| |
| const f_unreachable = builder.addFunction('unreachable', sig_index) |
| .addBody([kExprUnreachable]).index; |
| const f1 = builder.addFunction('f1', sig_index) |
| .addBody([kExprI32Const, v1]) |
| .index; |
| const f2 = builder.addFunction('f2', sig_index) |
| .addBody([kExprI32Const, v2]) |
| .index; |
| const f3 = builder.addFunction('f3', sig_index) |
| .addBody([kExprI32Const, v3]) |
| .index; |
| const f4 = builder.addFunction('f4', sig_index) |
| .addBody([kExprI32Const, v4]) |
| .index; |
| const f5 = builder.addFunction('f5', sig_index) |
| .addBody([kExprI32Const, v5]) |
| .index; |
| |
| builder.addFunction('call1', kSig_i_i) |
| .addBody([kExprLocalGet, 0, // function index |
| kExprCallIndirect, sig_index, table1]) |
| .exportAs('call1'); |
| builder.addFunction('return_call1', kSig_i_i) |
| .addBody([kExprLocalGet, 0, // function index |
| kExprReturnCallIndirect, sig_index, table1]) |
| .exportAs('return_call1'); |
| builder.addFunction('call2', kSig_i_i) |
| .addBody([kExprLocalGet, 0, // function index |
| kExprCallIndirect, sig_index, table2]) |
| .exportAs('call2'); |
| builder.addFunction('return_call2', kSig_i_i) |
| .addBody([kExprLocalGet, 0, // function index |
| kExprReturnCallIndirect, sig_index, table2]) |
| .exportAs('return_call2'); |
| |
| builder.addFunction('call_invalid_sig', kSig_i_i) |
| .addBody([kExprLocalGet, 0, kExprLocalGet, 0, // function index + param |
| kExprCallIndirect, other_sig, table2]) |
| .exportAs('call_invalid_sig'); |
| builder.addFunction('return_call_invalid_sig', kSig_i_i) |
| .addBody([kExprLocalGet, 0, kExprLocalGet, 0, // function index + param |
| kExprReturnCallIndirect, other_sig, table2]) |
| .exportAs('return_call_invalid_sig'); |
| |
| // We want to crash if we call through the table with index 0. |
| builder.addElementSegment(placeholder, 0, false, |
| [f_unreachable, f_unreachable, f_unreachable]); |
| builder.addElementSegment(table1, 0, false, [f1, f2, f3]); |
| // Keep one slot in table2 uninitialized. We should trap if we call it. |
| builder.addElementSegment(table2, 1, false, |
| [f_unreachable, f_unreachable, f4, f5]); |
| |
| const instance = builder.instantiate(); |
| |
| assertEquals(v1, instance.exports.call1(0)); |
| assertEquals(v2, instance.exports.call1(1)); |
| assertEquals(v3, instance.exports.call1(2)); |
| assertTraps(kTrapTableOutOfBounds, () => instance.exports.call1(3)); |
| assertEquals(v1, instance.exports.return_call1(0)); |
| assertEquals(v2, instance.exports.return_call1(1)); |
| assertEquals(v3, instance.exports.return_call1(2)); |
| assertTraps(kTrapTableOutOfBounds, () => instance.exports.return_call1(3)); |
| |
| // Try to call through the uninitialized table entry. |
| assertTraps(kTrapFuncSigMismatch, () => instance.exports.call2(0)); |
| assertEquals(v4, instance.exports.call2(3)); |
| assertEquals(v5, instance.exports.call2(4)); |
| assertTraps(kTrapFuncSigMismatch, |
| () => instance.exports.call_invalid_sig(4)); |
| assertTraps(kTrapFuncSigMismatch, () => instance.exports.return_call2(0)); |
| assertEquals(v4, instance.exports.return_call2(3)); |
| assertEquals(v5, instance.exports.return_call2(4)); |
| assertTraps(kTrapFuncSigMismatch, |
| () => instance.exports.return_call_invalid_sig(4)); |
| })(); |
| |
| (function IndirectCallToImportedNonZeroTable() { |
| print(arguments.callee.name); |
| |
| const table_size = 10; |
| const placeholder = new WebAssembly.Table( |
| { initial: table_size, maximum: table_size, element: "anyfunc" }); |
| const table = new WebAssembly.Table( |
| { initial: table_size, maximum: table_size, element: "anyfunc" }); |
| |
| const builder = new WasmModuleBuilder(); |
| builder.addImportedTable("m", "placeholder", table_size, table_size); |
| const t1 = builder.addImportedTable("m", "table", table_size, table_size); |
| |
| // We initialize the module twice and put the function f1 in the table at |
| // the index defined by {g}. Thereby we can initialize the table at different |
| // slots for different instances. The function f1 also returns {g} so that we |
| // can see that actually different functions get called. |
| const g = builder.addImportedGlobal("m", "base", kWasmI32); |
| |
| const sig_index = builder.addType(kSig_i_v); |
| const f1 = builder.addFunction("foo", sig_index) |
| .addBody([kExprGlobalGet, g, kExprI32Const, 12, kExprI32Add]); |
| |
| builder.addFunction('call', kSig_i_i) |
| .addBody([kExprLocalGet, 0, // function index |
| kExprCallIndirect, sig_index, t1]) |
| .exportAs('call'); |
| |
| builder.addElementSegment(t1, g, true, [f1.index]); |
| const base1 = 3; |
| const base2 = 5; |
| |
| const instance1 = builder.instantiate({ |
| m: { |
| placeholder: placeholder, |
| table: table, |
| base: base1 |
| } |
| }); |
| |
| const instance2 = builder.instantiate({ |
| m: { |
| placeholder: placeholder, |
| table: table, |
| base: base2 |
| } |
| }); |
| |
| assertEquals(base1 + 12, instance1.exports.call(base1)); |
| assertEquals(base2 + 12, instance1.exports.call(base2)); |
| assertEquals(base1 + 12, instance2.exports.call(base1)); |
| assertEquals(base2 + 12, instance2.exports.call(base2)); |
| })(); |
| |
| function js_div(a, b) { return (a / b) | 0; } |
| |
| (function CallImportedFunction() { |
| let kTableSize = 10; |
| print(arguments.callee.name); |
| |
| var builder = new WasmModuleBuilder(); |
| |
| let div = builder.addImport("q", "js_div", kSig_i_ii); |
| builder.addImportedTable("q", "placeholder", kTableSize, kTableSize); |
| let table_index = builder.addImportedTable("q", "table", kTableSize, kTableSize); |
| let g = builder.addImportedGlobal("q", "base", kWasmI32); |
| |
| let sig_index = builder.addType(kSig_i_ii); |
| builder.addFunction("placeholder", sig_index) |
| .addBody([kExprLocalGet, 0]); |
| |
| builder.addElementSegment(table_index, g, true, [div]); |
| builder.addFunction("main", kSig_i_ii) |
| .addBody([ |
| kExprI32Const, 55, // -- |
| kExprLocalGet, 0, // -- |
| kExprLocalGet, 1, // -- |
| kExprCallIndirect, 0, table_index]) // -- |
| .exportAs("main"); |
| |
| let m = new WebAssembly.Module(builder.toBuffer()); |
| |
| let table = new WebAssembly.Table({ |
| element: "anyfunc", |
| initial: kTableSize, |
| maximum: kTableSize |
| }); |
| let placeholder = new WebAssembly.Table({ |
| element: "anyfunc", |
| initial: kTableSize, |
| maximum: kTableSize |
| }); |
| |
| let instance = new WebAssembly.Instance(m, { |
| q: { |
| base: 0, table: table, placeholder: placeholder, |
| js_div: js_div |
| } |
| }); |
| |
| assertEquals(13, instance.exports.main(4, 0)); |
| })(); |