|  | // Copyright 2017 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. | 
|  |  | 
|  | load("test/mjsunit/wasm/wasm-module-builder.js"); | 
|  |  | 
|  | let kMaxTableSize = 10000000; | 
|  |  | 
|  | function addFunctions(builder) { | 
|  | let sig_index = builder.addType(kSig_i_ii); | 
|  | let mul = builder.addFunction("mul", sig_index) | 
|  | .addBody([ | 
|  | kExprLocalGet, 0,  // -- | 
|  | kExprLocalGet, 1,  // -- | 
|  | kExprI32Mul        // -- | 
|  | ]); | 
|  | let add = builder.addFunction("add", sig_index) | 
|  | .addBody([ | 
|  | kExprLocalGet, 0,  // -- | 
|  | kExprLocalGet, 1,  // -- | 
|  | kExprI32Add        // -- | 
|  | ]); | 
|  | let sub = builder.addFunction("sub", sig_index) | 
|  | .addBody([ | 
|  | kExprLocalGet, 0,  // -- | 
|  | kExprLocalGet, 1,  // -- | 
|  | kExprI32Sub        // -- | 
|  | ]); | 
|  | return {mul: mul, add: add, sub: sub}; | 
|  | } | 
|  |  | 
|  | function testBounds(func, table) { | 
|  | for (let i = 0; i < table.length; i++) { | 
|  | assertEquals(0, func(i)); | 
|  | } | 
|  | let l = table.length; | 
|  | let oob = [l, l + 1, l + 2, l * 2, l * 3, l + 10000]; | 
|  | for (let i of oob) { | 
|  | assertThrows(() => func(i)); | 
|  | } | 
|  | } | 
|  |  | 
|  | function addMain(builder) { | 
|  | builder.addImportedTable("imp", "table", 0, kMaxTableSize); | 
|  | builder.addFunction("main", kSig_i_i) | 
|  | .addBody([ | 
|  | kExprI32Const, 0, | 
|  | kExprLocalGet, 0, | 
|  | kExprCallIndirect, 0, kTableZero]) | 
|  | .exportAs("main"); | 
|  | } | 
|  |  | 
|  | let id = (() => {  // identity exported function | 
|  | let builder = new WasmModuleBuilder(); | 
|  | builder.addFunction("id", kSig_i_i) | 
|  | .addBody([kExprLocalGet, 0]) | 
|  | .exportAs("id"); | 
|  | let module = new WebAssembly.Module(builder.toBuffer()); | 
|  | return (new WebAssembly.Instance(builder.toModule())).exports.id; | 
|  | })(); | 
|  |  | 
|  | (function TableGrowBoundsCheck() { | 
|  | print("TableGrowBoundsCheck"); | 
|  | let builder = new WasmModuleBuilder(); | 
|  | addMain(builder); | 
|  | let module = new WebAssembly.Module(builder.toBuffer()); | 
|  | let table = new WebAssembly.Table({element: "anyfunc", | 
|  | initial: 1, maximum:kMaxTableSize}); | 
|  | function fillTable() { | 
|  | for (let i = 0; i < table.length; i++) table.set(i, id); | 
|  | return table; | 
|  | } | 
|  | fillTable(); | 
|  | let instance1 = new WebAssembly.Instance(module, {imp: {table:table}}); | 
|  | testBounds(instance1.exports.main, table); | 
|  |  | 
|  | for (let i = 0; i < 4; i++) { | 
|  | table.grow(1); | 
|  | fillTable(table); | 
|  | testBounds(instance1.exports.main, table); | 
|  | } | 
|  | let instance2 = new WebAssembly.Instance(module, {imp: {table:table}}); | 
|  | testBounds(instance2.exports.main, table); | 
|  |  | 
|  | for (let i = 0; i < 4; i++) { | 
|  | table.grow(1); | 
|  | fillTable(table); | 
|  | testBounds(instance1.exports.main, table); | 
|  | testBounds(instance2.exports.main, table); | 
|  | } | 
|  | })(); | 
|  |  | 
|  | (function TableGrowBoundsZeroInitial() { | 
|  | print("TableGrowBoundsZeroInitial"); | 
|  | let builder = new WasmModuleBuilder(); | 
|  | addMain(builder); | 
|  | let module = new WebAssembly.Module(builder.toBuffer()); | 
|  | var table = new WebAssembly.Table({element: "anyfunc", | 
|  | initial: 0, maximum:kMaxTableSize}); | 
|  | function growTableByOne() { | 
|  | table.grow(1); | 
|  | table.set(table.length - 1, id); | 
|  | } | 
|  | let instance1 = new WebAssembly.Instance(module, {imp: {table:table}}); | 
|  | testBounds(instance1.exports.main, table); | 
|  |  | 
|  | for (let i = 0; i < 4; i++) { | 
|  | growTableByOne(); | 
|  | testBounds(instance1.exports.main, table); | 
|  | } | 
|  | let instance2 = new WebAssembly.Instance(module, {imp: {table:table}}); | 
|  | testBounds(instance2.exports.main, table); | 
|  |  | 
|  | for (let i = 0; i < 4; i++) { | 
|  | growTableByOne(); | 
|  | testBounds(instance1.exports.main, table); | 
|  | testBounds(instance2.exports.main, table); | 
|  | } | 
|  | })(); | 
|  |  | 
|  | (function InstancesShareTableAndGrowTest() { | 
|  | print("InstancesShareTableAndGrowTest"); | 
|  | let builder = new WasmModuleBuilder(); | 
|  | let funcs = addFunctions(builder); | 
|  | builder.addFunction("main", kSig_i_ii) | 
|  | .addBody([ | 
|  | kExprI32Const, 15,  // -- | 
|  | kExprLocalGet, 0,   // -- | 
|  | kExprLocalGet, 1,   // -- | 
|  | kExprCallIndirect, 0, kTableZero])  // -- | 
|  | .exportAs("main"); | 
|  |  | 
|  | builder.addImportedTable("q", "table", 5, 32); | 
|  | let g = builder.addImportedGlobal("q", "base", kWasmI32); | 
|  | builder.addElementSegment(0, g, true, | 
|  | [funcs.mul.index, funcs.add.index, funcs.sub.index]); | 
|  | builder.addExportOfKind("table", kExternalTable, 0); | 
|  | let module = new WebAssembly.Module(builder.toBuffer()); | 
|  | let t1 = new WebAssembly.Table({element: "anyfunc", | 
|  | initial: 5, maximum: 30}); | 
|  |  | 
|  | for (let i = 0; i < 5; i++) { | 
|  | print("base = " + i); | 
|  | let instance = new WebAssembly.Instance(module, {q: {base: i, table: t1}}); | 
|  | main = instance.exports.main; | 
|  | assertEquals(i * 5 + 5, t1.length); | 
|  |  | 
|  | // mul | 
|  | assertEquals(15, main(1, i)); | 
|  | assertEquals(30, main(2, i)); | 
|  | // add | 
|  | assertEquals(20, main(5, i+1)); | 
|  | assertEquals(25, main(10, i+1)); | 
|  | //sub | 
|  | assertEquals(10, main(5, i+2)); | 
|  | assertEquals(5, main(10, i+2)); | 
|  |  | 
|  | assertThrows(() => t1.set(t1.length, id), RangeError); | 
|  | assertThrows(() => t1.set(t1.length + 5, id), RangeError); | 
|  | assertEquals(i * 5 + 5, t1.grow(5)); | 
|  | } | 
|  |  | 
|  | t1.set(t1.length - 1, id); | 
|  | assertThrows(() => t1.set(t1.length, id), RangeError); | 
|  | assertThrows(() => t1.grow(2), RangeError); | 
|  | })(); | 
|  |  | 
|  | (function ModulesShareTableAndGrowTest() { | 
|  | print("ModulesShareTableAndGrowTest"); | 
|  | let builder = new WasmModuleBuilder(); | 
|  | let sig_i_ii = builder.addType(kSig_i_ii); | 
|  | let sig_i_i = builder.addType(kSig_i_i); | 
|  | let sig_i_v = builder.addType(kSig_i_v); | 
|  |  | 
|  | let g1 = builder.addImportedGlobal("q", "base", kWasmI32); | 
|  |  | 
|  | let a = builder.addImport("q", "exp_add", sig_i_ii); | 
|  | let i = builder.addImport("q", "exp_inc", sig_i_i); | 
|  | let t = builder.addImport("q", "exp_ten", sig_i_v); | 
|  |  | 
|  | builder.setTableBounds(7, 35); | 
|  | builder.addElementSegment(0, g1, true, [a, i, t]); | 
|  |  | 
|  | builder.addExportOfKind("table", kExternalTable, 0); | 
|  | let module = new WebAssembly.Module(builder.toBuffer()); | 
|  |  | 
|  | function exp_add(a, b) { return a + b; } | 
|  | function exp_inc(a) { return a + 1 | 0; } | 
|  | function exp_ten() { return 10; } | 
|  |  | 
|  | let instance = new WebAssembly.Instance(module, {q: {base: 0, | 
|  | exp_add: exp_add, exp_inc: exp_inc, exp_ten: exp_ten}}); | 
|  |  | 
|  | let table = instance.exports.table; | 
|  |  | 
|  | print("   initial check"); | 
|  |  | 
|  | function checkTableFunc(index, expected, ...args) { | 
|  | let f = table.get(index); | 
|  | print("  table[" + index + "] = " + f); | 
|  | result = f(...args); | 
|  | print("    -> expect " + expected + ", got " + result); | 
|  | assertEquals(expected, result); | 
|  | } | 
|  |  | 
|  | checkTableFunc(0, 5, 1, 4); | 
|  | checkTableFunc(1, 9, 8); | 
|  | checkTableFunc(2, 10, 0); | 
|  |  | 
|  | let builder1 = new WasmModuleBuilder(); | 
|  | let g = builder1.addImportedGlobal("q", "base", kWasmI32); | 
|  | let funcs = addFunctions(builder1); | 
|  |  | 
|  | builder1.addImportedTable("q", "table", 6, 36); | 
|  | builder1.addElementSegment(0, g, true, | 
|  | [funcs.mul.index, funcs.add.index, funcs.sub.index]); | 
|  | let module1 = new WebAssembly.Module(builder1.toBuffer()); | 
|  |  | 
|  | function verifyTableFuncs(base) { | 
|  | print("  base = " + base); | 
|  | checkTableFunc(0, 5, 1, 4); | 
|  | checkTableFunc(1, 9, 8); | 
|  | checkTableFunc(2, 10, 0); | 
|  |  | 
|  | checkTableFunc(base+0, 20, 10, 2); // mul | 
|  | checkTableFunc(base+1, 12, 10, 2); // add | 
|  | checkTableFunc(base+2,  8, 10, 2); // sub | 
|  | } | 
|  |  | 
|  | for (let i = 3; i < 10; i++) { | 
|  | let instance1 = new WebAssembly.Instance(module1, {q: {base: i, table: table}}); | 
|  | verifyTableFuncs(i); | 
|  | var prev = table.length; | 
|  | assertEquals(prev,     table.grow(3)); | 
|  | assertEquals(prev + 3, table.length); | 
|  | verifyTableFuncs(i); | 
|  |  | 
|  | assertThrows(() => table.set(table.length, id), RangeError); | 
|  | assertThrows(() => table.set(table.length + 5, id), RangeError); | 
|  | } | 
|  | })(); | 
|  |  | 
|  | (function ModulesInstancesSharedTableBoundsCheck() { | 
|  | print("ModulesInstancesSharedTableBoundsCheck"); | 
|  | let table = new WebAssembly.Table({element: "anyfunc", | 
|  | initial: 1, maximum: kMaxTableSize}); | 
|  |  | 
|  | function CallModuleBuilder() { | 
|  | var builder = new WasmModuleBuilder(); | 
|  | builder.addType(kSig_i_v); | 
|  | builder.addType(kSig_v_v); | 
|  | let index_i_ii = builder.addType(kSig_i_ii); | 
|  | let index_i_i = builder.addType(kSig_i_i); | 
|  | builder.addImportedTable("x", "table", 1, kMaxTableSize); | 
|  | builder.addFunction("add", index_i_ii) | 
|  | .addBody([ | 
|  | kExprLocalGet, 0, | 
|  | kExprLocalGet, 1, | 
|  | kExprI32Add]); | 
|  | builder.addFunction("main", index_i_i) | 
|  | .addBody([ | 
|  | kExprI32Const, 5, | 
|  | kExprI32Const, 5, | 
|  | kExprLocalGet, 0, | 
|  | kExprCallIndirect, index_i_ii, kTableZero]) | 
|  | .exportAs("main"); | 
|  | builder.addElementSegment(0, 0, false, [0]); | 
|  | return new WebAssembly.Module(builder.toBuffer()); | 
|  | } | 
|  |  | 
|  | var instances = [], modules = []; | 
|  | modules[0] = CallModuleBuilder(); | 
|  | modules[1] = CallModuleBuilder(); | 
|  |  | 
|  | // Modules[0] shared by instances[0..2], modules[1] shared by instances[3, 4] | 
|  | instances[0] = new WebAssembly.Instance(modules[0], {x: {table:table}}); | 
|  | instances[1] = new WebAssembly.Instance(modules[0], {x: {table:table}}); | 
|  | instances[2] = new WebAssembly.Instance(modules[0], {x: {table:table}}); | 
|  | instances[3] = new WebAssembly.Instance(modules[1], {x: {table:table}}); | 
|  | instances[4] = new WebAssembly.Instance(modules[1], {x: {table:table}}); | 
|  |  | 
|  | function VerifyTableBoundsCheck(size) { | 
|  | print("Verifying bounds for size = " + size); | 
|  | assertEquals(size, table.length); | 
|  | for (let i = 0; i < 5; i++) { | 
|  | // Validity check for indirect call | 
|  | assertEquals(10, instances[i].exports.main(0)); | 
|  | // Bounds check at different out of bounds indices | 
|  | assertInvalidFunction = function(s) { | 
|  | assertThrows( | 
|  | () => instances[i].exports.main(s), WebAssembly.RuntimeError, | 
|  | kTrapMsgs[kTrapTableOutOfBounds]); | 
|  | } | 
|  | assertInvalidFunction(size); | 
|  | assertInvalidFunction(size + 1); | 
|  | assertInvalidFunction(size + 1000); | 
|  | assertInvalidFunction(2 * size); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (let i = 0; i < 4; i++) { | 
|  | VerifyTableBoundsCheck(99900 * i + 1); | 
|  | table.grow(99900); | 
|  | } | 
|  | })(); |