[wasm] Table.Grow should grow dispatch tables

- Table.Grow updates function, signature table sizes
 - Updates generated code with new base addresses for function, signature tables
 - Relocates size references for correct bounds check

R=bradnelson@chromium.org, titzer@chromium.org

Review-Url: https://codereview.chromium.org/2637643002
Cr-Commit-Position: refs/heads/master@{#42349}
This commit is contained in:
gdeepti 2017-01-14 23:46:08 -08:00 committed by Commit bot
parent 6fad1ad27d
commit ccf0998d63
7 changed files with 154 additions and 8 deletions

View File

@ -546,7 +546,10 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
isolate->ThrowException(e);
return;
}
int new_size = static_cast<int>(new_size64);
i::WasmTableObject::Grow(i_isolate, receiver,
static_cast<uint32_t>(new_size - old_size));
if (new_size != old_size) {
i::Handle<i::FixedArray> new_array =
@ -557,7 +560,9 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
receiver->set_functions(*new_array);
}
// TODO(titzer): update relevant instances.
// TODO(gdeepti): use weak links for instances
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(old_size);
}
void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {

View File

@ -147,6 +147,19 @@ void RelocateGlobals(Handle<FixedArray> code_table, Address old_start,
}
}
void RelocateTableSizeReferences(Handle<FixedArray> 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> code = Handle<Code>(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<Code> 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<WasmInstanceObject> instance,
}
}
void wasm::GrowDispatchTables(Isolate* isolate,
Handle<FixedArray> 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<FixedArray> old_function_table(
FixedArray::cast(dispatch_tables->get(i + 2)));
Handle<FixedArray> old_signature_table(
FixedArray::cast(dispatch_tables->get(i + 3)));
Handle<FixedArray> new_function_table =
isolate->factory()->CopyFixedArrayAndGrow(old_function_table, count);
Handle<FixedArray> new_signature_table =
isolate->factory()->CopyFixedArrayAndGrow(old_signature_table, count);
// Get code table for the instance
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(dispatch_tables->get(i)));
Handle<FixedArray> 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> code = Handle<Code>(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<WasmModuleObject> module_obj,
int instance_count) {

View File

@ -440,6 +440,9 @@ int32_t GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
int index, Handle<JSFunction> js_function);
void GrowDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
uint32_t old_size, uint32_t count);
namespace testing {
void ValidateInstancesChain(Isolate* isolate,

View File

@ -186,6 +186,13 @@ WasmTableObject* WasmTableObject::cast(Object* object) {
return reinterpret_cast<WasmTableObject*>(object);
}
void WasmTableObject::Grow(Isolate* isolate, Handle<WasmTableObject> table,
uint32_t count) {
Handle<FixedArray> dispatch_tables(table->dispatch_tables());
wasm::GrowDispatchTables(isolate, dispatch_tables,
table->functions()->length(), count);
}
Handle<WasmMemoryObject> WasmMemoryObject::New(Isolate* isolate,
Handle<JSArrayBuffer> buffer,
int maximum) {

View File

@ -65,7 +65,8 @@ class WasmTableObject : public JSObject {
static Handle<WasmTableObject> New(Isolate* isolate, uint32_t initial,
uint32_t maximum,
Handle<FixedArray>* js_functions);
static bool Grow(Handle<WasmTableObject> table, uint32_t count);
static void Grow(Isolate* isolate, Handle<WasmTableObject> table,
uint32_t count);
static Handle<FixedArray> AddDispatchTable(
Isolate* isolate, Handle<WasmTableObject> table,
Handle<WasmInstanceObject> instance, int table_index,
@ -91,7 +92,8 @@ class WasmMemoryObject : public JSObject {
Handle<JSArrayBuffer> buffer,
int maximum);
static bool Grow(Handle<WasmMemoryObject> memory, uint32_t count);
static bool Grow(Isolate* isolate, Handle<WasmMemoryObject> memory,
uint32_t count);
};
// Representation of a WebAssembly.Instance JavaScript-level object.

View File

@ -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));
})();

View File

@ -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);