Commit 4a90e722 authored by rossberg's avatar rossberg Committed by Commit bot

[wasm] Implement Table#set and Table#grow

R=ahaas@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/2414053002
Cr-Commit-Position: refs/heads/master@{#40357}
parent fbef7e14
......@@ -366,6 +366,8 @@ bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
return false;
}
const int max_table_size = 1 << 26;
void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
......@@ -392,7 +394,6 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
return;
}
}
const int max_table_size = 1 << 26;
// The descriptor's 'initial'.
int initial;
if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
......@@ -511,10 +512,44 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
"Receiver is not a WebAssembly.Table")) {
return;
}
// TODO(rossberg): grow table and update relevant instances.
v8::Local<v8::Value> e =
v8::Exception::TypeError(v8_str(isolate, "Table#grow unimplemented"));
isolate->ThrowException(e);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::JSObject> receiver =
i::Handle<i::JSObject>::cast(Utils::OpenHandle(*args.This()));
i::Handle<i::FixedArray> old_array(
i::FixedArray::cast(
receiver->GetInternalField(kWasmTableArrayFieldIndex)),
i_isolate);
int old_size = old_array->length();
int64_t new_size64 = 0;
if (args.Length() > 0 && !args[0]->IntegerValue(context).To(&new_size64)) {
return;
}
new_size64 += old_size;
i::Handle<i::Object> max_val(
receiver->GetInternalField(kWasmTableMaximumFieldIndex), i_isolate);
int max_size =
max_val->IsSmi() ? i::Smi::cast(*max_val)->value() : max_table_size;
if (new_size64 < old_size || new_size64 > max_size) {
v8::Local<v8::Value> e = v8::Exception::RangeError(
v8_str(isolate, new_size64 < old_size ? "trying to shrink table"
: "maximum table size exceeded"));
isolate->ThrowException(e);
return;
}
int new_size = static_cast<int>(new_size64);
if (new_size != old_size) {
i::Handle<i::FixedArray> new_array =
i_isolate->factory()->NewFixedArray(new_size);
for (int i = 0; i < old_size; ++i) new_array->set(i, old_array->get(i));
i::Object* null = i_isolate->heap()->null_value();
for (int i = old_size; i < new_size; ++i) new_array->set(i, null);
receiver->SetInternalField(kWasmTableArrayFieldIndex, *new_array);
}
// TODO(titzer): update relevant instances.
}
void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
......@@ -535,15 +570,21 @@ void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
int i = 0;
if (args.Length() > 0 && !args[0]->Int32Value(context).To(&i)) return;
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
if (i >= 0 && i < i::Handle<i::FixedArray>::cast(array)->length()) {
i::Handle<i::Object> value(i::Handle<i::FixedArray>::cast(array)->get(i),
i_isolate);
return_value.Set(Utils::ToLocal(value));
if (i < 0 || i >= i::Handle<i::FixedArray>::cast(array)->length()) {
v8::Local<v8::Value> e =
v8::Exception::RangeError(v8_str(isolate, "index out of bounds"));
isolate->ThrowException(e);
return;
}
i::Handle<i::Object> value(i::Handle<i::FixedArray>::cast(array)->get(i),
i_isolate);
return_value.Set(Utils::ToLocal(value));
}
void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
Local<Context> context = isolate->GetCurrentContext();
i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()),
......@@ -551,19 +592,39 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
"Receiver is not a WebAssembly.Table")) {
return;
}
if (args.Length() < 2 ||
!(args[1]->IsNull() ||
(args[1]->IsObject() && v8::Object::Cast(*args[1])->IsCallable()))) {
if (args.Length() < 2) {
v8::Local<v8::Value> e = v8::Exception::TypeError(
v8_str(isolate, "Argument 1 must be null or a function"));
isolate->ThrowException(e);
return;
}
i::Handle<i::Object> value = Utils::OpenHandle(*args[1]);
if (!value->IsNull(i_isolate) &&
(!value->IsJSFunction() ||
i::Handle<i::JSFunction>::cast(value)->code()->kind() !=
i::Code::JS_TO_WASM_FUNCTION)) {
v8::Local<v8::Value> e = v8::Exception::TypeError(
v8_str(isolate, "Argument 1 must be null or a WebAssembly function"));
isolate->ThrowException(e);
return;
}
// TODO(rossberg): set table element and update relevent instances.
v8::Local<v8::Value> e =
v8::Exception::TypeError(v8_str(isolate, "Table#set unimplemented"));
isolate->ThrowException(e);
i::Handle<i::JSObject> receiver =
i::Handle<i::JSObject>::cast(Utils::OpenHandle(*args.This()));
i::Handle<i::Object> array(
receiver->GetInternalField(kWasmTableArrayFieldIndex), i_isolate);
int i;
if (!args[0]->Int32Value(context).To(&i)) return;
if (i < 0 || i >= i::Handle<i::FixedArray>::cast(array)->length()) {
v8::Local<v8::Value> e =
v8::Exception::RangeError(v8_str(isolate, "index out of bounds"));
isolate->ThrowException(e);
return;
}
i::Handle<i::FixedArray>::cast(array)->set(i, *value);
// TODO(titzer): update relevant instances.
}
void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
......
......@@ -6,6 +6,9 @@
'use strict';
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
// Basic tests.
var outOfUint32RangeValue = 1e12;
......@@ -129,19 +132,101 @@ function assertTableIsValid(table) {
(function TestGet() {
let table = new WebAssembly.Table({element: "anyfunc", initial: 10});
for (let i = 0; i < 10; ++i) {
for (let i = 0; i < table.length; ++i) {
assertEquals(null, table.get(i));
assertEquals(null, table.get(String(i)));
}
assertEquals(null, table.get(""));
assertEquals(null, table.get(NaN));
assertEquals(null, table.get({}));
assertEquals(null, table.get([]));
assertEquals(null, table.get(() => {}));
assertEquals(undefined, table.get(10));
assertEquals(undefined, table.get(-1));
for (let key of [0.4, "", NaN, {}, [], () => {}]) {
assertEquals(null, table.get(key));
}
for (let key of [-1, table.length, table.length * 10]) {
assertThrows(() => table.get(key), RangeError);
}
assertThrows(() => table.get(Symbol()), TypeError);
assertThrows(() => WebAssembly.Table.prototype.get.call([], 0), TypeError);
})();
(function TestSet() {
let builder = new WasmModuleBuilder;
builder.addExport("wasm", builder.addFunction("", kSig_v_v));
builder.addExport("host", builder.addImportWithModule("test", "f", kSig_v_v));
let {wasm, host} = builder.instantiate({test: {f() {}}}).exports;
let table = new WebAssembly.Table({element: "anyfunc", initial: 10});
for (let f of [wasm, host]) {
for (let i = 0; i < table.length; ++i) table.set(i, null);
for (let i = 0; i < table.length; ++i) {
assertSame(null, table.get(i));
assertSame(undefined, table.set(i, f));
assertSame(f, table.get(i));
}
for (let i = 0; i < table.length; ++i) table.set(i, null);
for (let i = 0; i < table.length; ++i) {
assertSame(null, table.get(i));
assertSame(undefined, table.set(String(i), f));
assertSame(f, table.get(i));
}
for (let key of [0.4, "", NaN, {}, [], () => {}]) {
assertSame(undefined, table.set(0, null));
assertSame(undefined, table.set(key, f));
assertSame(f, table.get(0));
}
for (let key of [-1, table.length, table.length * 10]) {
assertThrows(() => table.set(key, f), RangeError);
}
assertThrows(() => table.set(0), TypeError);
for (let val of [undefined, 0, "", {}, [], () => {}]) {
assertThrows(() => table.set(0, val), TypeError);
}
assertThrows(() => table.set(Symbol(), f), TypeError);
assertThrows(() => WebAssembly.Table.prototype.set.call([], 0, f),
TypeError);
}
})();
(function TestGrow() {
let builder = new WasmModuleBuilder;
builder.addExport("wasm", builder.addFunction("", kSig_v_v));
builder.addExport("host", builder.addImportWithModule("test", "f", kSig_v_v));
let {wasm, host} = builder.instantiate({test: {f() {}}}).exports;
function init(table) {
for (let i = 0; i < 5; ++i) table.set(i, wasm);
for (let i = 15; i < 20; ++i) table.set(i, host);
}
function check(table) {
for (let i = 0; i < 5; ++i) assertSame(wasm, table.get(i));
for (let i = 6; i < 15; ++i) assertSame(null, table.get(i));
for (let i = 15; i < 20; ++i) assertSame(host, table.get(i));
for (let i = 21; i < table.length; ++i) assertSame(null, table.get(i));
}
let table = new WebAssembly.Table({element: "anyfunc", initial: 20});
init(table);
check(table);
table.grow(0);
check(table);
table.grow(10);
check(table);
assertThrows(() => table.grow(-10), RangeError);
table = new WebAssembly.Table({element: "anyfunc", initial: 20, maximum: 25});
init(table);
check(table);
table.grow(0);
check(table);
table.grow(5);
check(table);
table.grow(0);
check(table);
assertThrows(() => table.grow(1), RangeError);
assertThrows(() => table.grow(-10), RangeError);
assertThrows(() => WebAssembly.Table.prototype.grow.call([], 0), TypeError);
})();
......@@ -87,7 +87,7 @@ class WasmFunctionBuilder {
}
exportAs(name) {
this.module.exports.push({name: name, kind: kExternalFunction, index: this.index});
this.module.addExport(name, this.index);
return this;
}
......@@ -206,6 +206,11 @@ class WasmModuleBuilder {
return this;
}
addExport(name, index) {
this.exports.push({name: name, kind: kExternalFunction, index: index});
return this;
}
addDataSegment(addr, data, is_global = false) {
this.segments.push({addr: addr, data: data, is_global: is_global});
return this.segments.length - 1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment