diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc index f2c630dd4a..69ee0813ea 100644 --- a/src/wasm/wasm-js.cc +++ b/src/wasm/wasm-js.cc @@ -546,7 +546,10 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo& args) { isolate->ThrowException(e); return; } + int new_size = static_cast(new_size64); + i::WasmTableObject::Grow(i_isolate, receiver, + static_cast(new_size - old_size)); if (new_size != old_size) { i::Handle new_array = @@ -557,7 +560,9 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo& args) { receiver->set_functions(*new_array); } - // TODO(titzer): update relevant instances. + // TODO(gdeepti): use weak links for instances + v8::ReturnValue return_value = args.GetReturnValue(); + return_value.Set(old_size); } void WebAssemblyTableGet(const v8::FunctionCallbackInfo& args) { diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc index b3c2d433e1..74fa1bf930 100644 --- a/src/wasm/wasm-module.cc +++ b/src/wasm/wasm-module.cc @@ -147,6 +147,19 @@ void RelocateGlobals(Handle code_table, Address old_start, } } +void RelocateTableSizeReferences(Handle code_table, + uint32_t old_size, uint32_t new_size) { + for (int i = 0; i < code_table->length(); ++i) { + DCHECK(code_table->get(i)->IsCode()); + Handle code = Handle(Code::cast(code_table->get(i))); + AllowDeferredHandleDereference embedding_raw_address; + int mask = 1 << RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE; + for (RelocIterator it(*code, mask); !it.done(); it.next()) { + it.rinfo()->update_wasm_function_table_size_reference(old_size, new_size); + } + } +} + Handle CreatePlaceholder(Factory* factory, Code::Kind kind) { byte buffer[] = {0, 0, 0, 0}; // fake instructions. CodeDesc desc = { @@ -2343,6 +2356,42 @@ int32_t wasm::GrowMemory(Isolate* isolate, Handle instance, } } +void wasm::GrowDispatchTables(Isolate* isolate, + Handle dispatch_tables, + uint32_t old_size, uint32_t count) { + DCHECK_EQ(0, dispatch_tables->length() % 4); + for (int i = 0; i < dispatch_tables->length(); i += 4) { + Handle old_function_table( + FixedArray::cast(dispatch_tables->get(i + 2))); + Handle old_signature_table( + FixedArray::cast(dispatch_tables->get(i + 3))); + Handle new_function_table = + isolate->factory()->CopyFixedArrayAndGrow(old_function_table, count); + Handle new_signature_table = + isolate->factory()->CopyFixedArrayAndGrow(old_signature_table, count); + + // Get code table for the instance + Handle instance( + WasmInstanceObject::cast(dispatch_tables->get(i))); + Handle code_table(instance->compiled_module()->code_table()); + + // Relocate size references + RelocateTableSizeReferences(code_table, old_size, old_size + count); + + // Replace references of old tables with new tables. + for (int j = 0; j < code_table->length(); ++j) { + if (!code_table->get(j)->IsCode()) continue; + Handle code = Handle(Code::cast(code_table->get(j))); + ReplaceReferenceInCode(code, old_function_table, new_function_table); + ReplaceReferenceInCode(code, old_signature_table, new_signature_table); + } + + // Update dispatch tables with new function/signature tables + dispatch_tables->set(i + 2, *new_function_table); + dispatch_tables->set(i + 3, *new_signature_table); + } +} + void testing::ValidateInstancesChain(Isolate* isolate, Handle module_obj, int instance_count) { diff --git a/src/wasm/wasm-module.h b/src/wasm/wasm-module.h index ff680711df..1de06db25a 100644 --- a/src/wasm/wasm-module.h +++ b/src/wasm/wasm-module.h @@ -440,6 +440,9 @@ int32_t GrowMemory(Isolate* isolate, Handle instance, void UpdateDispatchTables(Isolate* isolate, Handle dispatch_tables, int index, Handle js_function); +void GrowDispatchTables(Isolate* isolate, Handle dispatch_tables, + uint32_t old_size, uint32_t count); + namespace testing { void ValidateInstancesChain(Isolate* isolate, diff --git a/src/wasm/wasm-objects.cc b/src/wasm/wasm-objects.cc index 55882f34a9..42c2a27ffa 100644 --- a/src/wasm/wasm-objects.cc +++ b/src/wasm/wasm-objects.cc @@ -186,6 +186,13 @@ WasmTableObject* WasmTableObject::cast(Object* object) { return reinterpret_cast(object); } +void WasmTableObject::Grow(Isolate* isolate, Handle table, + uint32_t count) { + Handle dispatch_tables(table->dispatch_tables()); + wasm::GrowDispatchTables(isolate, dispatch_tables, + table->functions()->length(), count); +} + Handle WasmMemoryObject::New(Isolate* isolate, Handle buffer, int maximum) { diff --git a/src/wasm/wasm-objects.h b/src/wasm/wasm-objects.h index 435cff6e30..8c22ca3893 100644 --- a/src/wasm/wasm-objects.h +++ b/src/wasm/wasm-objects.h @@ -65,7 +65,8 @@ class WasmTableObject : public JSObject { static Handle New(Isolate* isolate, uint32_t initial, uint32_t maximum, Handle* js_functions); - static bool Grow(Handle table, uint32_t count); + static void Grow(Isolate* isolate, Handle table, + uint32_t count); static Handle AddDispatchTable( Isolate* isolate, Handle table, Handle instance, int table_index, @@ -91,7 +92,8 @@ class WasmMemoryObject : public JSObject { Handle buffer, int maximum); - static bool Grow(Handle memory, uint32_t count); + static bool Grow(Isolate* isolate, Handle memory, + uint32_t count); }; // Representation of a WebAssembly.Instance JavaScript-level object. diff --git a/test/mjsunit/wasm/indirect-tables.js b/test/mjsunit/wasm/indirect-tables.js index d17946e596..2ccc4a5d18 100644 --- a/test/mjsunit/wasm/indirect-tables.js +++ b/test/mjsunit/wasm/indirect-tables.js @@ -371,7 +371,89 @@ function js_div(a, b) { return (a / b) | 0; } } } } - - - +})(); + +(function TableGrowBoundsCheck() { + print("TableGrowBoundsCheck"); + var kMaxSize = 30, kInitSize = 5; + let table = new WebAssembly.Table({element: "anyfunc", + initial: kInitSize, maximum: kMaxSize}); + var builder = new WasmModuleBuilder(); + builder.addImportedTable("x", "table", kInitSize, kMaxSize); + let module = new WebAssembly.Module(builder.toBuffer()); + let instance = new WebAssembly.Instance(module, {x: {base: 1, table: table}}); + + for(var i = kInitSize; i < kMaxSize; i+=5) { + assertEquals(i, table.length); + for (var j = 0; j < i; j++) table.set(j, null); + for (var j = 0; j < i; j++) assertEquals(null, table.get(j)); + assertThrows(() => table.set(i, null)); + assertThrows(() => table.get(i)); + assertEquals(i, table.grow(5)); + } + assertEquals(30, table.length); + assertThrows(() => table.grow(1)); + assertThrows(() => table.set(kMaxSize, null)); + assertThrows(() => table.get(kMaxSize)); +})(); + +(function CumulativeGrowTest() { + print("CumulativeGrowTest..."); + let table = new WebAssembly.Table({ + element: "anyfunc", initial: 10, maximum: 30}); + var builder = new WasmModuleBuilder(); + builder.addImportedTable("x", "table", 10, 30); + + let g = builder.addImportedGlobal("x", "base", kWasmI32); + let sig_index = builder.addType(kSig_i_v); + builder.addFunction("g", sig_index) + .addBody([ + kExprGetGlobal, g + ]); + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprGetLocal, 0, + kExprCallIndirect, sig_index, kTableZero]) // -- + .exportAs("main"); + builder.addFunctionTableInit(g, true, [g]); + let module = new WebAssembly.Module(builder.toBuffer()); + + var instances = []; + for (var i = 0; i < 10; i++) { + print(" base = " + i); + instances.push(new WebAssembly.Instance( + module, {x: {base: i, table: table}})); + } + + for (var j = 0; j < 10; j++) { + let func = table.get(j); + assertEquals("function", typeof func); + assertEquals(j, func()); + assertEquals(j, instances[j].exports.main(j)); + } + + assertEquals(10, table.grow(10)); + + // Verify that grow does not alter function behaviors + for (var j = 0; j < 10; j++) { + let func = table.get(j); + assertEquals("function", typeof func); + assertEquals(j, func()); + assertEquals(j, instances[j].exports.main(j)); + } + + let new_builder = new WasmModuleBuilder(); + new_builder.addExport("wasm", new_builder.addFunction("", kSig_v_v)); + new_builder.addImportedTable("x", "table", 20, 30); + let new_module = new WebAssembly.Module(new_builder.toBuffer()); + let instance = new WebAssembly.Instance(new_module, {x: {table: table}}); + let new_func = instance.exports.wasm; + + for (var j = 10; j < 20; j++) { + table.set(j, new_func); + let func = table.get(j); + assertEquals("function", typeof func); + assertSame(new_func, table.get(j)); + } + assertThrows(() => table.grow(11)); })(); diff --git a/test/mjsunit/wasm/js-api.js b/test/mjsunit/wasm/js-api.js index 812fb06b8d..d7a3ea1358 100644 --- a/test/mjsunit/wasm/js-api.js +++ b/test/mjsunit/wasm/js-api.js @@ -508,7 +508,6 @@ assertEq(tblGrowDesc.enumerable, false); assertEq(tblGrowDesc.configurable, true); // 'WebAssembly.Table.prototype.grow' method -if (false) { // TODO: Table.grow let tblGrow = tblGrowDesc.value; assertEq(tblGrow.length, 1); assertErrorMessage(() => tblGrow.call(), TypeError, /called on incompatible undefined/); @@ -522,7 +521,6 @@ assertEq(tbl.length, 1); assertEq(tbl.grow(1), 1); assertEq(tbl.length, 2); assertErrorMessage(() => tbl.grow(1), Error, /failed to grow table/); -} // 'WebAssembly.validate' function assertErrorMessage(() => WebAssembly.validate(), TypeError);