// 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: --experimental-wasm-bulk-memory load("test/mjsunit/wasm/wasm-module-builder.js"); (function TestTableCopyInbounds() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); let sig_v_iii = builder.addType(kSig_v_iii); let kTableSize = 5; builder.setTableBounds(kTableSize, kTableSize); builder.addFunction("copy", sig_v_iii) .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, kNumericPrefix, kExprTableCopy, kTableZero, kTableZero]) .exportAs("copy"); let instance = builder.instantiate(); let copy = instance.exports.copy; for (let i = 0; i <= kTableSize; i++) { copy(0, 0, i); // nop copy(0, i, kTableSize - i); copy(i, 0, kTableSize - i); } })(); function addFunction(builder, k) { let m = builder.addFunction("", kSig_i_v) .addBody([...wasmI32Const(k)]); return m; } function addFunctions(builder, count) { let o = {}; for (var i = 0; i < count; i++) { o[`f${i}`] = addFunction(builder, i); } return o; } function assertTable(obj, ...elems) { for (var i = 0; i < elems.length; i++) { assertEquals(elems[i], obj.get(i)); } } (function TestTableCopyElems() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); let sig_v_iii = builder.addType(kSig_v_iii); let kTableSize = 5; builder.setTableBounds(kTableSize, kTableSize); { let o = addFunctions(builder, kTableSize); builder.addElementSegment(0, false, [o.f0.index, o.f1.index, o.f2.index, o.f3.index, o.f4.index]); } builder.addFunction("copy", sig_v_iii) .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, kNumericPrefix, kExprTableCopy, kTableZero, kTableZero]) .exportAs("copy"); builder.addExportOfKind("table", kExternalTable, 0); let instance = builder.instantiate(); let table = instance.exports.table; let f0 = table.get(0), f1 = table.get(1), f2 = table.get(2), f3 = table.get(3), f4 = table.get(4); let copy = instance.exports.copy; assertEquals(0, f0()); assertEquals(1, f1()); assertEquals(2, f2()); assertTable(table, f0, f1, f2, f3, f4); copy(0, 1, 1); assertTable(table, f1, f1, f2, f3, f4); copy(0, 1, 2); assertTable(table, f1, f2, f2, f3, f4); copy(3, 0, 2); assertTable(table, f1, f2, f2, f1, f2); copy(1, 0, 2); assertTable(table, f1, f1, f2, f1, f2); })(); function assertCall(call, ...elems) { for (var i = 0; i < elems.length; i++) { assertEquals(elems[i], call(i)); } } (function TestTableCopyCalls() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); let sig_v_iii = builder.addType(kSig_v_iii); let sig_i_i = builder.addType(kSig_i_i); let sig_i_v = builder.addType(kSig_i_v); let kTableSize = 5; builder.setTableBounds(kTableSize, kTableSize); { let o = addFunctions(builder, 5); builder.addElementSegment(0, false, [o.f0.index, o.f1.index, o.f2.index, o.f3.index, o.f4.index]); } builder.addFunction("copy", sig_v_iii) .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, kNumericPrefix, kExprTableCopy, kTableZero, kTableZero]) .exportAs("copy"); builder.addFunction("call", sig_i_i) .addBody([ kExprGetLocal, 0, kExprCallIndirect, sig_i_v, kTableZero]) .exportAs("call"); let instance = builder.instantiate(); let copy = instance.exports.copy; let call = instance.exports.call; assertCall(call, 0, 1, 2, 3, 4); copy(0, 1, 1); assertCall(call, 1, 1, 2, 3, 4); copy(0, 1, 2); assertCall(call, 1, 2, 2, 3, 4); copy(3, 0, 2); assertCall(call, 1, 2, 2, 1, 2); })(); (function TestTableCopyOobWrites() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); let sig_v_iii = builder.addType(kSig_v_iii); let kTableSize = 5; builder.setTableBounds(kTableSize, kTableSize); { let o = addFunctions(builder, kTableSize); builder.addElementSegment(0, false, [o.f0.index, o.f1.index, o.f2.index]); } builder.addFunction("copy", sig_v_iii) .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, kNumericPrefix, kExprTableCopy, kTableZero, kTableZero]) .exportAs("copy"); builder.addExportOfKind("table", kExternalTable, 0); let instance = builder.instantiate(); let table = instance.exports.table; let f0 = table.get(0), f1 = table.get(1), f2 = table.get(2); let copy = instance.exports.copy; // Non-overlapping, src < dst. assertThrows(() => copy(3, 0, 3)); assertTable(table, f0, f1, f2, f0, f1); // Non-overlapping, dst < src. assertThrows(() => copy(0, 4, 2)); assertTable(table, f1, f1, f2, f0, f1); // Overlapping, src < dst. This is required to copy backward, but the first // access will be out-of-bounds, so nothing changes. assertThrows(() => copy(3, 0, 99)); assertTable(table, f1, f1, f2, f0, f1); // Overlapping, dst < src. assertThrows(() => copy(0, 1, 99)); assertTable(table, f1, f2, f0, f1, f1); })(); (function TestTableCopyOob1() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); let sig_v_iii = builder.addType(kSig_v_iii); let kTableSize = 5; builder.setTableBounds(kTableSize, kTableSize); builder.addFunction("copy", sig_v_iii) .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, kNumericPrefix, kExprTableCopy, kTableZero, kTableZero]) .exportAs("copy"); let instance = builder.instantiate(); let copy = instance.exports.copy; copy(0, 0, 1); // nop copy(0, 0, kTableSize); // nop assertThrows(() => copy(0, 0, kTableSize+1)); assertThrows(() => copy(1, 0, kTableSize)); assertThrows(() => copy(0, 1, kTableSize)); { let big = 1000000; assertThrows(() => copy(big, 0, 0)); assertThrows(() => copy(0, big, 0)); } for (let big = 4294967295; big > 1000; big >>>= 1) { assertThrows(() => copy(big, 0, 1)); assertThrows(() => copy(0, big, 1)); assertThrows(() => copy(0, 0, big)); } for (let big = -1000; big != 0; big <<= 1) { assertThrows(() => copy(big, 0, 1)); assertThrows(() => copy(0, big, 1)); assertThrows(() => copy(0, 0, big)); } })(); (function TestTableCopyShared() { print(arguments.callee.name); let kTableSize = 5; let table = new WebAssembly.Table({element: "anyfunc", initial: kTableSize, maximum: kTableSize}); let module = (() => { let builder = new WasmModuleBuilder(); let sig_v_iii = builder.addType(kSig_v_iii); let sig_i_i = builder.addType(kSig_i_i); let sig_i_v = builder.addType(kSig_i_v); builder.addImportedTable("m", "table", kTableSize, kTableSize); var g = builder.addImportedGlobal("m", "g", kWasmI32); for (let i = 0; i < kTableSize; i++) { let f = builder.addFunction("", kSig_i_v) .addBody([ kExprGetGlobal, g, ...wasmI32Const(i), kExprI32Add ]); f.exportAs(`f${i}`); } builder.addFunction("copy", sig_v_iii) .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, kNumericPrefix, kExprTableCopy, kTableZero, kTableZero]) .exportAs("copy"); builder.addFunction("call", sig_i_i) .addBody([ kExprGetLocal, 0, kExprCallIndirect, sig_i_v, kTableZero]) .exportAs("call"); return builder.toModule(); })(); // Two different instances with different globals, to verify that // dispatch tables get updated with the right instance. let x = new WebAssembly.Instance(module, {m: {g: 1000, table: table}}); let y = new WebAssembly.Instance(module, {m: {g: 2000, table: table}}); let x_call = x.exports.call; let y_call = y.exports.call; assertNotEquals(x.exports.f3, y.exports.f3); table.set(0, x.exports.f0); table.set(1, x.exports.f1); table.set(2, x.exports.f2); table.set(3, y.exports.f3); table.set(4, y.exports.f4); assertEquals(2003, table.get(3)(3)); assertEquals(2003, x_call(3)); assertEquals(2003, y_call(3)); // Check that calling copy on either of them updates the dispatch table // on both of them. assertCall(x_call, 1000, 1001, 1002, 2003, 2004); assertCall(y_call, 1000, 1001, 1002, 2003, 2004); x.exports.copy(0, 1, 1); assertCall(x_call, 1001, 1001, 1002, 2003, 2004); assertCall(y_call, 1001, 1001, 1002, 2003, 2004); y.exports.copy(0, 1, 2); assertCall(x_call, 1001, 1002, 1002, 2003, 2004); assertCall(y_call, 1001, 1002, 1002, 2003, 2004); x.exports.copy(3, 0, 2); assertCall(x_call, 1001, 1002, 1002, 1001, 1002); assertCall(y_call, 1001, 1002, 1002, 1001, 1002); })();