WebAssembly: implement the table section and table import
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 13 Dec 2016 20:32:40 +0000 (20:32 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 13 Dec 2016 20:32:40 +0000 (20:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=165716

Reviewed by Keith Miller.

JSTests:

* wasm/Builder.js:
(const._importMemoryContinuation):
(const._importTableContinuation):
(export.default.Builder.prototype._registerSectionBuilders.switch.case.string_appeared_here.this.section):
(const._importMemoryContinuation.section): Deleted.
(const): Deleted.
(const._importMemoryContinuation.assert): Deleted.
* wasm/Builder_WebAssemblyBinary.js:
(const.putResizableLimits):
(const.putTable):
(const.emitters.Import):
(const.emitters.Table):
* wasm/function-tests/call-indirect-params.js:
* wasm/function-tests/call-indirect.js:
* wasm/function-tests/table-basic-2.js: Added.
(import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.makeInstance):
(func):
* wasm/function-tests/table-basic.js: Added.
(import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.makeInstance):
* wasm/js-api/call-indirect-results.js:
(const.wasmModuleWhichImportJS): Deleted.
(MonomorphicImport): Deleted.
* wasm/js-api/call-indirect.js:
(const.wasmModuleWhichImportJS):
(const.makeTable):
(Polyphic2Import):
(VirtualImport):
(MonomorphicImport): Deleted.
* wasm/js-api/table.js: Added.
(assertBadBinary):
(assert.truthy):
(assertBadTable):
(assertBadTableImport):
(assertBadBinary.assertBadTableInstance):
(assertBadTableInstance):
(new.WebAssembly.Table):
* wasm/js-api/test_basic_api.js:
(const.c.in.constructorProperties.switch):

Source/JavaScriptCore:

This patch implements the Table space for wasm:
https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#table-section

It only implements defining and importing a table. The bulk
of this patch is implementing the various wasm Table prototype
methods and the underlying Table object:
https://github.com/WebAssembly/design/blob/master/JS.md#webassemblytable-constructor

This patch also fixes a bug in our implementation with call_indirect.
We initially implemented call_indirect as a way to call functions that
are imported or defined in the module. This was the wrong
interpretation of the spec. Instead, call_indirect can only index into
the table index space.

* JavaScriptCore.xcodeproj/project.pbxproj:
* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::B3IRGenerator):
(JSC::Wasm::B3IRGenerator::addCallIndirect):
(JSC::Wasm::parseAndCompile):
* wasm/WasmFormat.h:
(JSC::Wasm::TableInformation::TableInformation):
(JSC::Wasm::TableInformation::operator bool):
(JSC::Wasm::TableInformation::isImport):
(JSC::Wasm::TableInformation::initial):
(JSC::Wasm::TableInformation::maximum):
(JSC::Wasm::CallableFunction::CallableFunction):
* wasm/WasmFunctionParser.h:
(JSC::Wasm::FunctionParser<Context>::parseExpression):
* wasm/WasmModuleParser.cpp:
(JSC::Wasm::ModuleParser::parseImport):
(JSC::Wasm::ModuleParser::parseResizableLimits):
(JSC::Wasm::ModuleParser::parseTableHelper):
(JSC::Wasm::ModuleParser::parseTable):
(JSC::Wasm::ModuleParser::parseMemoryHelper):
(JSC::Wasm::ModuleParser::parseExport):
* wasm/WasmModuleParser.h:
* wasm/js/JSWebAssemblyHelpers.h: Added.
(JSC::toNonWrappingUint32):
* wasm/js/JSWebAssemblyInstance.cpp:
(JSC::JSWebAssemblyInstance::visitChildren):
* wasm/js/JSWebAssemblyInstance.h:
(JSC::JSWebAssemblyInstance::table):
(JSC::JSWebAssemblyInstance::setTable):
(JSC::JSWebAssemblyInstance::offsetOfTable):
* wasm/js/JSWebAssemblyTable.cpp:
(JSC::JSWebAssemblyTable::create):
(JSC::JSWebAssemblyTable::JSWebAssemblyTable):
(JSC::JSWebAssemblyTable::visitChildren):
(JSC::JSWebAssemblyTable::grow):
(JSC::JSWebAssemblyTable::clearFunction):
(JSC::JSWebAssemblyTable::setFunction):
* wasm/js/JSWebAssemblyTable.h:
(JSC::JSWebAssemblyTable::maximum):
(JSC::JSWebAssemblyTable::size):
(JSC::JSWebAssemblyTable::getFunction):
(JSC::JSWebAssemblyTable::offsetOfSize):
(JSC::JSWebAssemblyTable::offsetOfFunctions):
(JSC::JSWebAssemblyTable::isValidSize):
* wasm/js/WebAssemblyFunction.cpp:
(JSC::WebAssemblyFunction::call):
(JSC::WebAssemblyFunction::create):
(JSC::WebAssemblyFunction::visitChildren):
(JSC::WebAssemblyFunction::finishCreation):
* wasm/js/WebAssemblyFunction.h:
(JSC::WebAssemblyFunction::signature):
(JSC::WebAssemblyFunction::wasmEntrypoint):
(JSC::WebAssemblyFunction::webAssemblyCallee): Deleted.
* wasm/js/WebAssemblyInstanceConstructor.cpp:
(JSC::constructJSWebAssemblyInstance):
* wasm/js/WebAssemblyMemoryConstructor.cpp:
(JSC::constructJSWebAssemblyMemory):
* wasm/js/WebAssemblyModuleRecord.cpp:
(JSC::WebAssemblyModuleRecord::finishCreation):
(JSC::WebAssemblyModuleRecord::link):
* wasm/js/WebAssemblyTableConstructor.cpp:
(JSC::constructJSWebAssemblyTable):
* wasm/js/WebAssemblyTablePrototype.cpp:
(JSC::getTable):
(JSC::webAssemblyTableProtoFuncLength):
(JSC::webAssemblyTableProtoFuncGrow):
(JSC::webAssemblyTableProtoFuncGet):
(JSC::webAssemblyTableProtoFuncSet):
(JSC::WebAssemblyTablePrototype::create):
(JSC::WebAssemblyTablePrototype::finishCreation):
* wasm/js/WebAssemblyTablePrototype.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@209771 268f45cc-cd09-0410-ab3c-d52691b4dbfc

31 files changed:
JSTests/ChangeLog
JSTests/wasm/Builder.js
JSTests/wasm/Builder_WebAssemblyBinary.js
JSTests/wasm/function-tests/call-indirect-params.js
JSTests/wasm/function-tests/call-indirect.js
JSTests/wasm/function-tests/table-basic-2.js [new file with mode: 0644]
JSTests/wasm/function-tests/table-basic.js [new file with mode: 0644]
JSTests/wasm/js-api/call-indirect-results.js
JSTests/wasm/js-api/call-indirect.js
JSTests/wasm/js-api/table.js [new file with mode: 0644]
JSTests/wasm/js-api/test_basic_api.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmFunctionParser.h
Source/JavaScriptCore/wasm/WasmModuleParser.cpp
Source/JavaScriptCore/wasm/WasmModuleParser.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyHelpers.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.h
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.h
Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyMemoryConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.h

index 4344ef1..68e8f70 100644 (file)
@@ -1,3 +1,49 @@
+2016-12-13  Saam Barati  <sbarati@apple.com>
+
+        WebAssembly: implement the table section and table import
+        https://bugs.webkit.org/show_bug.cgi?id=165716
+
+        Reviewed by Keith Miller.
+
+        * wasm/Builder.js:
+        (const._importMemoryContinuation):
+        (const._importTableContinuation):
+        (export.default.Builder.prototype._registerSectionBuilders.switch.case.string_appeared_here.this.section):
+        (const._importMemoryContinuation.section): Deleted.
+        (const): Deleted.
+        (const._importMemoryContinuation.assert): Deleted.
+        * wasm/Builder_WebAssemblyBinary.js:
+        (const.putResizableLimits):
+        (const.putTable):
+        (const.emitters.Import):
+        (const.emitters.Table):
+        * wasm/function-tests/call-indirect-params.js:
+        * wasm/function-tests/call-indirect.js:
+        * wasm/function-tests/table-basic-2.js: Added.
+        (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.makeInstance):
+        (func):
+        * wasm/function-tests/table-basic.js: Added.
+        (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.makeInstance):
+        * wasm/js-api/call-indirect-results.js:
+        (const.wasmModuleWhichImportJS): Deleted.
+        (MonomorphicImport): Deleted.
+        * wasm/js-api/call-indirect.js:
+        (const.wasmModuleWhichImportJS):
+        (const.makeTable):
+        (Polyphic2Import):
+        (VirtualImport):
+        (MonomorphicImport): Deleted.
+        * wasm/js-api/table.js: Added.
+        (assertBadBinary):
+        (assert.truthy):
+        (assertBadTable):
+        (assertBadTableImport):
+        (assertBadBinary.assertBadTableInstance):
+        (assertBadTableInstance):
+        (new.WebAssembly.Table):
+        * wasm/js-api/test_basic_api.js:
+        (const.c.in.constructorProperties.switch):
+
 2016-12-13  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r209725.
index 0f7ab97..ad7a53b 100644 (file)
@@ -103,10 +103,19 @@ const _importFunctionContinuation = (builder, section, nextBuilder) => {
 };
 
 const _importMemoryContinuation = (builder, section, nextBuilder) => {
-    return (module, field, memoryDescription) => {
-        assert.isString(module, `Import function module should be a string, got "${module}"`);
-        assert.isString(field, `Import function field should be a string, got "${field}"`);
-        section.data.push({module, field, kind: "Memory", memoryDescription});
+    return (module, field, {initial, maximum}) => {
+        assert.isString(module, `Import Memory module should be a string, got "${module}"`);
+        assert.isString(field, `Import Memory field should be a string, got "${field}"`);
+        section.data.push({module, field, kind: "Memory", memoryDescription: {initial, maximum}});
+        return nextBuilder;
+    };
+};
+
+const _importTableContinuation = (builder, section, nextBuilder) => {
+    return (module, field, {initial, maximum, element}) => {
+        assert.isString(module, `Import Table module should be a string, got "${module}"`);
+        assert.isString(field, `Import Table field should be a string, got "${field}"`);
+        section.data.push({module, field, kind: "Table", tableDescription: {initial, maximum, element}});
         return nextBuilder;
     };
 };
@@ -299,12 +308,31 @@ const _createFunction = (section, builder, previousBuilder) => {
 
         if (typeof(signature) === "undefined")
             signature = { params: [] };
-        assert.hasObjectProperty(signature, "params", `Expect function signature to be an object with a "params" field, got "${signature}"`);
-        const [params, ret] = _normalizeFunctionSignature(signature.params, signature.ret);
-        signature = { params: params, ret: ret };
+
+        let type;
+        let params;
+        if (typeof signature === "object") {
+            assert.hasObjectProperty(signature, "params", `Expect function signature to be an object with a "params" field, got "${signature}"`);
+            let ret;
+            ([params, ret] = _normalizeFunctionSignature(signature.params, signature.ret));
+            signature = {params, ret};
+            type = _maybeRegisterType(builder, signature);
+        } else {
+            assert.truthy(typeof signature === "number");
+            const typeSection = builder._getSection("Type");
+            assert.truthy(!!typeSection);
+            // FIXME: we should allow indices that exceed this to be able to
+            // test JSCs validator is correct. https://bugs.webkit.org/show_bug.cgi?id=165786
+            assert.truthy(signature < typeSection.data.length);
+            type = signature;
+            signature = typeSection.data[signature];
+            assert.hasObjectProperty(signature, "params", `Expect function signature to be an object with a "params" field, got "${signature}"`);
+            params = signature.params;
+        }
+
         const func = {
             name: functionName,
-            type: _maybeRegisterType(builder, signature),
+            type,
             signature: signature,
             locals: params.concat(locals), // Parameters are the first locals.
             parameterCount: params.length,
@@ -379,11 +407,11 @@ export default class Builder {
                     const s = this._addSection(section);
                     const importBuilder = {
                         End: () => this,
-                        Table: () => { throw new Error(`Unimplemented: import table`); },
                         Global: () => { throw new Error(`Unimplemented: import global`); },
                     };
                     importBuilder.Function = _importFunctionContinuation(this, s, importBuilder);
                     importBuilder.Memory = _importMemoryContinuation(this, s, importBuilder);
+                    importBuilder.Table = _importTableContinuation(this, s, importBuilder);
                     return importBuilder;
                 };
                 break;
@@ -400,8 +428,17 @@ export default class Builder {
                 break;
 
             case "Table":
-                // FIXME Implement table https://bugs.webkit.org/show_bug.cgi?id=164135
-                this[section] = () => { throw new Error(`Unimplemented: section type "${section}"`); };
+                this[section] = function() {
+                    const s = this._addSection(section);
+                    const exportBuilder = {
+                        End: () => this,
+                        Table: ({initial, maximum, element}) => {
+                            s.data.push({tableDescription: {initial, maximum, element}});
+                            return exportBuilder;
+                        }
+                    };
+                    return exportBuilder;
+                };
                 break;
 
             case "Memory":
index d3ab5e5..cebcb4a 100644 (file)
@@ -29,6 +29,28 @@ import * as WASM from 'WASM.js';
 
 const put = (bin, type, value) => bin[type](value);
 
+const putResizableLimits = (bin, initial, maximum) => {
+    assert.truthy(typeof initial === "number", "We expect 'initial' to be an integer");
+    let hasMaximum = 0;
+    if (typeof maximum === "number") {
+        hasMaximum = 1;
+    } else {
+        assert.truthy(typeof maximum === "undefined", "We expect 'maximum' to be an integer if it's defined");
+    }
+
+    put(bin, "varuint1", hasMaximum);
+    put(bin, "varuint32", initial);
+    if (hasMaximum)
+        put(bin, "varuint32", maximum);
+};
+
+const putTable = (bin, {initial, maximum, element}) => {
+    assert.truthy(WASM.isValidType(element), "We expect 'element' to be a valid type. It was: " + element);
+    put(bin, "varint7", WASM.typeValue[element]);
+
+    putResizableLimits(bin, initial, maximum);
+};
+
 const emitters = {
     Type: (section, bin) => {
         put(bin, "varuint32", section.data.length);
@@ -57,23 +79,13 @@ const emitters = {
                 put(bin, "varuint32", entry.type);
                 break;
             }
-            case "Table": throw new Error(`Not yet implemented`);
+            case "Table": {
+                putTable(bin, entry.tableDescription);
+                break;
+            }
             case "Memory": {
                 let {initial, maximum} = entry.memoryDescription;
-                assert.truthy(typeof initial === "number", "We expect 'initial' to be a number");
-                initial |= 0;
-                let hasMaximum = 0;
-                if (typeof maximum === "number") {
-                    maximum |= 0;
-                    hasMaximum = 1;
-                } else {
-                    assert.truthy(typeof maximum === "undefined", "We expect 'maximum' to be a number if it's defined");
-                }
-
-                put(bin, "varuint1", hasMaximum);
-                put(bin, "varuint32", initial);
-                if (hasMaximum)
-                    put(bin, "varuint32", maximum);
+                putResizableLimits(bin, initial, maximum);
                 break;
             };
             case "Global": throw new Error(`Not yet implemented`);
@@ -87,7 +99,12 @@ const emitters = {
             put(bin, "varuint32", signature);
     },
 
-    Table: (section, bin) => { throw new Error(`Not yet implemented`); },
+    Table: (section, bin) => {
+        put(bin, "varuint32", section.data.length);
+        for (const {tableDescription} of section.data) {
+            putTable(bin, tableDescription);
+        }
+    },
 
     Memory: (section, bin) => {
         // Flags, currently can only be [0,1]
index 4148211..e69de29 100644 (file)
@@ -1,28 +0,0 @@
-import Builder from '../Builder.js'
-
-const b = new Builder();
-b.Type().End()
-    .Function().End()
-    .Code()
-
-    .Function({ params: ["i32"], ret: "i32" })
-    .I32Const(1)
-    .End()
-
-    .Function({ params: ["i32"], ret: "i32" })
-    .GetLocal(0)
-    .End()
-
-    .Function({ params: ["i32", "i32"], ret: "i32" })
-    .GetLocal(1)
-    .GetLocal(0)
-    .CallIndirect(0, 0)
-    .End()
-
-
-const bin = b.WebAssembly()
-bin.trim();
-testWasmModuleFunctions(bin.get(), 3, [], [],
-                        [[{ type: "i32", value: 1 }, [{ type: "i32", value: 0 }, { type: "i32", value: 4 }]],
-                         [{ type: "i32", value: 4 }, [{ type: "i32", value: 1 }, { type: "i32", value: 4 }]],
-                        ]);
index a4b5fbe..e69de29 100644 (file)
@@ -1,27 +0,0 @@
-import Builder from '../Builder.js'
-
-const b = new Builder();
-b.Type().End()
-    .Function().End()
-    .Code()
-
-    .Function({ params: [], ret: "i32" })
-    .I32Const(1)
-    .End()
-
-    .Function({ params: [], ret: "i32" })
-    .I32Const(2)
-    .End()
-
-    .Function({ params: ["i32"], ret: "i32" })
-    .GetLocal(0)
-    .CallIndirect(0, 0)
-    .End()
-
-
-const bin = b.WebAssembly()
-bin.trim();
-testWasmModuleFunctions(bin.get(), 3, [], [],
-                        [[{ type: "i32", value: 1 }, [{ type: "i32", value: 0 }]],
-                         [{ type: "i32", value: 1 }, [{ type: "i32", value: 0 }]],
-                        ]);
diff --git a/JSTests/wasm/function-tests/table-basic-2.js b/JSTests/wasm/function-tests/table-basic-2.js
new file mode 100644 (file)
index 0000000..27361f4
--- /dev/null
@@ -0,0 +1,57 @@
+import Builder from '../Builder.js'
+import * as assert from '../assert.js'
+
+function makeInstance(func) {
+    const builder = new Builder()
+        .Type()
+            .Func(["i32", "i32"], "i32")
+            .Func(["i32"], "i32")
+        .End()
+        .Import()
+            .Table("imp", "table", {initial: 20, element: "anyfunc"})
+            .Function("imp", "func", { params: ["i32"], ret: "i32" })
+        .End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+            .Function("bar")
+        .End()
+        .Code()
+            .Function("foo", 0 /*['i32', 'i32'] => 'i32'*/)
+                .GetLocal(1) // parameter to call
+                .GetLocal(0) // call index
+                .CallIndirect(1, 0) // calling function of type ['i32'] => 'i32'
+                .Return()
+            .End()
+            .Function("bar", 1 /*['i32'] => 'i32'*/)
+                .GetLocal(0)
+                .Call(0)
+                .Return()
+            .End()
+        .End();
+
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const table = new WebAssembly.Table({initial: 20, element: "anyfunc"});
+    return {instance: new WebAssembly.Instance(module, {imp: {table, func}}), table};
+}
+
+{
+    let i;
+    function func(x) {
+        if (x !== i)
+            throw new Error("Bad argument");
+        return x + 44;
+    }
+    const {instance, table} = makeInstance(func);
+    const exports = instance.exports;
+    const foo = exports.foo;
+    table.set(0, exports.bar);
+    assert.eq(table.get(0), exports.bar);
+
+    for (i = 0; i < 10000; i++) {
+        if (foo(0, i) !== i + 44)
+            throw new Error("Bad call indirect");
+    }
+}
diff --git a/JSTests/wasm/function-tests/table-basic.js b/JSTests/wasm/function-tests/table-basic.js
new file mode 100644 (file)
index 0000000..537b1fe
--- /dev/null
@@ -0,0 +1,67 @@
+import Builder from '../Builder.js'
+import * as assert from '../assert.js'
+
+function makeInstance() {
+    const builder = new Builder()
+        .Type()
+            .Func(["i32", "i32"], "i32")
+            .Func(["i32"], "i32")
+        .End()
+        .Import()
+            .Table("imp", "table", {initial: 20, element: "anyfunc"})
+        .End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+            .Function("bar")
+        .End()
+        .Code()
+            .Function("foo", 0 /*['i32', 'i32'] => 'i32'*/)
+                .GetLocal(1) // parameter to call
+                .GetLocal(0) // call index
+                .CallIndirect(1, 0) // calling function of type ['i32'] => 'i32'
+                .Return()
+            .End()
+            .Function("bar", 1 /*['i32'] => 'i32'*/)
+                .GetLocal(0)
+                .I32Const(42)
+                .I32Add()
+                .Return()
+            .End()
+        .End();
+
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const table = new WebAssembly.Table({initial: 20, element: "anyfunc"});
+    return {instance: new WebAssembly.Instance(module, {imp: {table}}), table};
+}
+
+{
+    const {instance, table} = makeInstance();
+    const exports = instance.exports;
+    const foo = exports.foo;
+    table.set(0, exports.bar);
+    assert.eq(table.get(0), exports.bar);
+
+    for (let i = 0; i < 1000; i++) {
+        if (foo(0, i) !== i + 42)
+            throw new Error("Bad call indirect");
+    }
+}
+
+// FIXME: make this work cross module. The reason it doesn't
+// now is that we don't unique Signature*.
+// https://bugs.webkit.org/show_bug.cgi?id=165511
+/*
+{
+    const {instance, table} = makeInstance();
+    const foo = instance.exports.foo;
+    //table.set(0, makeInstance().instance.exports.bar); // Cross instance function.
+
+    for (let i = 0; i < 1000; i++) {
+        if (foo(0, i) !== i + 42)
+            throw new Error("Bad call indirect");
+    }
+}
+*/
index 040198e..e69de29 100644 (file)
@@ -1,41 +0,0 @@
-import * as assert from '../assert.js';
-import Builder from '../Builder.js';
-
-const wasmModuleWhichImportJS = () => {
-    const builder = (new Builder())
-        .Type().End()
-        .Import()
-            .Function("imp", "func", { params: ["i32"], ret: "i32" })
-        .End()
-        .Function().End()
-        .Export()
-            .Function("changeCounter")
-        .End()
-        .Code()
-            .Function("changeCounter", { params: ["i32", "i32"], ret: "i32" })
-                .I32Const(42)
-                .GetLocal(0)
-                .I32Add()
-                .GetLocal(1)
-                .CallIndirect(0, 0) // Calls func(param[0] + 42).
-                .I32Const(0)
-                .CallIndirect(0, 0) // Calls func(param[0] + 42).
-            .End()
-        .End();
-    const bin = builder.WebAssembly().get();
-    const module = new WebAssembly.Module(bin);
-    return module;
-};
-
-
-(function MonomorphicImport() {
-    let counter = 0;
-    const counterSetter = v => counter = v;
-    const module = wasmModuleWhichImportJS();
-    const instance = new WebAssembly.Instance(module, { imp: { func: counterSetter } });
-    for (let i = 0; i < 4096; ++i) {
-        // Invoke this a bunch of times to make sure the IC in the wasm -> JS stub works correctly.
-        instance.exports.changeCounter(i, 0);
-        assert.eq(counter, i + 42);
-    }
-})();
index b827ef9..01cd42f 100644 (file)
@@ -6,10 +6,12 @@ const wasmModuleWhichImportJS = () => {
         .Type().End()
         .Import()
             .Function("imp", "func", { params: ["i32"] })
+            .Table("imp", "table", { initial: 1, maximum: 1, element: "anyfunc"})
         .End()
         .Function().End()
         .Export()
             .Function("changeCounter")
+            .Function("callFunc")
         .End()
         .Code()
             .Function("changeCounter", { params: ["i32", "i32"] })
@@ -17,7 +19,11 @@ const wasmModuleWhichImportJS = () => {
                 .GetLocal(0)
                 .I32Add()
                 .GetLocal(1)
-                .CallIndirect(0, 0) // Calls func(param[0] + 42).
+                .CallIndirect(0, 0) // Calls table[0](param[0] + 42).
+            .End()
+            .Function("callFunc", { params: ["i32"] })
+                .GetLocal(0)
+                .Call(0) // Calls func(param[0] + 42)
             .End()
         .End();
     const bin = builder.WebAssembly().get();
@@ -25,12 +31,17 @@ const wasmModuleWhichImportJS = () => {
     return module;
 };
 
+const makeTable = () => {
+    return new WebAssembly.Table({initial: 1, maximum: 1, element: "anyfunc"});
+};
 
 (function MonomorphicImport() {
     let counter = 0;
     const counterSetter = v => counter = v;
+    const table = makeTable();
     const module = wasmModuleWhichImportJS();
-    const instance = new WebAssembly.Instance(module, { imp: { func: counterSetter } });
+    const instance = new WebAssembly.Instance(module, { imp: { func: counterSetter, table} });
+    table.set(0, instance.exports.callFunc);
     for (let i = 0; i < 4096; ++i) {
         // Invoke this a bunch of times to make sure the IC in the wasm -> JS stub works correctly.
         instance.exports.changeCounter(i, 0);
@@ -44,8 +55,14 @@ const wasmModuleWhichImportJS = () => {
     const counterASetter = v => counterA = v;
     const counterBSetter = v => counterB = { valueB: v };
     const module = wasmModuleWhichImportJS();
-    const instanceA = new WebAssembly.Instance(module, { imp: { func: counterASetter } });
-    const instanceB = new WebAssembly.Instance(module, { imp: { func: counterBSetter } });
+
+    const tableA = makeTable();
+    const instanceA = new WebAssembly.Instance(module, { imp: { func: counterASetter, table: tableA} });
+    tableA.set(0, instanceA.exports.callFunc);
+
+    const tableB = makeTable();
+    const instanceB = new WebAssembly.Instance(module, { imp: { func: counterBSetter, table: tableB} });
+    tableB.set(0, instanceB.exports.callFunc);
     for (let i = 0; i < 2048; ++i) {
         instanceA.exports.changeCounter(i, 0);
         assert.isA(counterA, "number");
@@ -57,8 +74,6 @@ const wasmModuleWhichImportJS = () => {
 })();
 
 (function VirtualImport() {
-    const num = 10; // It's definitely going virtual at 10!
-    let counters = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
     const counterSetters = [
         v => counters[0] = v,
         v => counters[1] = v + 1,
@@ -71,12 +86,16 @@ const wasmModuleWhichImportJS = () => {
         v => counters[8] = v + 8,
         v => counters[9] = v + 9,
     ];
+    const num = counterSetters.length;
+    let counters = counterSetters.map(() => 0);
     assert.eq(counters.length, num);
-    assert.eq(counterSetters.length, num);
     const module = wasmModuleWhichImportJS();
     let instances = [];
-    for (let i = 0; i < num; ++i)
-        instances[i] = new WebAssembly.Instance(module, { imp: { func: counterSetters[i] } });
+    for (let i = 0; i < num; ++i) {
+        let table = makeTable();
+        instances[i] = new WebAssembly.Instance(module, { imp: { func: counterSetters[i], table} });
+        table.set(0, instances[i].exports.callFunc);
+    }
     for (let i = 0; i < 2048; ++i) {
         for (let j = 0; j < num; ++j) {
             instances[j].exports.changeCounter(i, 0);
diff --git a/JSTests/wasm/js-api/table.js b/JSTests/wasm/js-api/table.js
new file mode 100644 (file)
index 0000000..d339fd5
--- /dev/null
@@ -0,0 +1,230 @@
+import Builder from '../Builder.js';
+import * as assert from '../assert.js';
+
+const badTableString = "couldn't parse section Table";
+const badImportString = "couldn't parse section Import";
+function assertBadBinary(builder, str) {
+    const bin = builder.WebAssembly().get();
+    let threw = false;
+    try {
+        new WebAssembly.Module(bin);
+    } catch(e) {
+        threw = true;
+        assert.truthy(e.toString().indexOf(str) !== -1);
+        assert.truthy(e instanceof WebAssembly.CompileError);
+    }
+    assert.truthy(threw);
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Import()
+            .Table("imp", "table", {initial: 20, element: "anyfunc"})
+        .End()
+        .Function().End()
+        .Table()
+            .Table({initial: 20, maximum: 30, element: "anyfunc"})
+        .End()
+        .Code()
+        .End();
+    assertBadBinary(builder, badTableString);
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Function().End()
+        .Table()
+            .Table({initial: 20, maximum: 30, element: "anyfunc"})
+            .Table({initial: 20, maximum: 30, element: "anyfunc"})
+        .End()
+        .Code()
+        .End();
+    assertBadBinary(builder, badTableString);
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", {params: ["i32"]})
+                .GetLocal(0)
+                .CallIndirect(0, 0)
+            .End()
+        .End();
+    assertBadBinary(builder, "call_indirect is only valid when a table is defined or imported");
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Function().End()
+        .Table()
+            .Table({initial:20, element:"anyfunc"})
+        .End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", {params: ["i32"]})
+                .GetLocal(0)
+                .CallIndirect(0, 1)
+            .End()
+        .End();
+    assertBadBinary(builder, "call_indirect 'reserved' varuint1 must be 0x0");
+}
+
+function assertBadTable(tableDescription) {
+    const builder = new Builder()
+        .Type().End()
+        .Function().End()
+        .Table()
+            .Table(tableDescription)
+        .End()
+        .Code()
+        .End();
+    assertBadBinary(builder, badTableString);
+}
+
+function assertBadTableImport(tableDescription) {
+    const builder = new Builder()
+        .Type().End()
+        .Import()
+            .Table("imp", "table", tableDescription)
+        .End()
+        .Function().End()
+        .Code()
+        .End();
+    assertBadBinary(builder, badImportString);
+}
+
+{
+    let badDescriptions = [
+        {initial: 10, element: "i32"},
+        {initial: 10, element: "f32"},
+        {initial: 10, element: "f64"},
+        {initial: 10, element: "i64"},
+        {initial: 10, maximum: 20, element: "i32"},
+        {initial: 10, maximum: 20, element: "f32"},
+        {initial: 10, maximum: 20, element: "f64"},
+        {initial: 10, maximum: 20, element: "i64"},
+
+        {initial: 10, maximum: 9, element: "anyfunc"},
+        {initial: 1, maximum: 0, element: "anyfunc"},
+        {initial: 2**32 - 1, maximum: 2**32 - 2, element: "anyfunc"},
+        {initial: 2**31, element: "anyfunc"},
+    ];
+
+    for (const d of badDescriptions) {
+        assertBadTable(d);
+        assertBadTableImport(d);
+    }
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Import()
+            .Table("imp", "table", {initial: 20, element: "anyfunc"})
+            .Table("imp", "table", {initial: 20, element: "anyfunc"})
+        .End()
+        .Function().End()
+        .Code()
+        .End();
+    assertBadBinary(builder, badImportString);
+}
+
+
+{
+    function assertBadTableInstance(tableDescription, table, message) {
+        const builder = new Builder()
+            .Type().End()
+            .Import()
+                .Table("imp", "table", tableDescription)
+            .End()
+            .Function().End()
+            .Code()
+            .End();
+
+        let threw = false;
+        const module = new WebAssembly.Module(builder.WebAssembly().get());
+        try {
+            new WebAssembly.Instance(module, {imp: {table}});
+        } catch (e) {
+            assert.eq(e.toString(), message);
+            threw = true;
+        }
+        assert.truthy(threw);
+    }
+
+    const badTables = [
+        [{initial: 100, maximum:100, element:"anyfunc"}, new WebAssembly.Table({initial:100, element: "anyfunc"}), "TypeError: Table import does not have a 'maximum' but the module requires that it does"],
+        [{initial: 100, maximum:100, element:"anyfunc"}, new WebAssembly.Table({initial:100, maximum:101, element: "anyfunc"}), "TypeError: Imported Table's 'maximum' is larger than the module's expected 'maximum'"],
+        [{initial: 100, element:"anyfunc"}, new WebAssembly.Table({initial:10, element: "anyfunc"}), "TypeError: Table import provided an 'initial' that is too small"],
+        [{initial: 10, element:"anyfunc"}, new WebAssembly.Table({initial:9, element: "anyfunc"}), "TypeError: Table import provided an 'initial' that is too small"],
+    ];
+    for (const [d, t, m] of badTables) {
+        assertBadTableInstance(d, t, m);
+    }
+}
+
+{
+    {
+        const table = new WebAssembly.Table({element: "anyfunc", initial: 20, maximum: 30});
+        table.grow(30);
+        assert.throws(() => table.grow(31), TypeError, "WebAssembly.Table.prototype.grow could not grow the table");
+        assert.throws(() => table.grow(29), TypeError, "WebAssembly.Table.prototype.grow could not grow the table");
+    }
+
+    {
+        const table = new WebAssembly.Table({element: "anyfunc", initial: 20});
+        assert.throws(() => table.grow({valueOf() { return 19; }}), TypeError, "WebAssembly.Table.prototype.grow could not grow the table");
+        let called = false;
+        table.grow({valueOf() { called = true; return 21; }});
+        assert.truthy(called);
+    }
+
+    {
+        const table = new WebAssembly.Table({element: "anyfunc", initial: 20});
+        assert.throws(() => table.get(20), RangeError, "WebAssembly.Table.prototype.get expects an integer less than the size of the table");
+        for (let i = 0; i < 20; i++)
+            assert.eq(table.get(i), null);
+    }
+
+    {
+        const table = new WebAssembly.Table({element: "anyfunc", initial: 20});
+        assert.throws(() => table.set(20, null), RangeError, "WebAssembly.Table.prototype.set expects an integer less than the size of the table");
+        for (let i = 0; i < 20; i++)
+            table.set(i, null);
+    }
+
+    {
+        // This should not throw
+        new WebAssembly.Table({initial: 2**20, maximum: 2**32 - 1, element: "anyfunc"});
+    }
+}
+
+{
+
+    function assertBadTable(table) {
+        const builder = new Builder()
+            .Type().End()
+            .Import()
+                .Table("imp", "table", {initial: 25, element: "anyfunc"})
+            .End()
+            .Function().End()
+            .Code()
+            .End();
+        const module = new WebAssembly.Module(builder.WebAssembly().get());
+        assert.throws(() => new WebAssembly.Instance(module, {imp: {table}}), TypeError, "Table import is not an instance of WebAssembly.Table");
+    }
+    assertBadTable(25);
+    assertBadTable(new Object);
+    assertBadTable([]);
+    assertBadTable(new WebAssembly.Memory({initial:1}));
+}
index 9560036..7b3d137 100644 (file)
@@ -78,8 +78,9 @@ for (const c in constructorProperties) {
         new WebAssembly.Memory({initial: 20});
         break;
     case "Table":
-        // FIXME Implement and test these APIs further. For now they just throw. https://bugs.webkit.org/show_bug.cgi?id=159775
-        assert.throws(() => new WebAssembly[c](), Error, `WebAssembly doesn't yet implement the ${c} constructor property`);
+        new WebAssembly.Table({initial: 20, element: "anyfunc"});
+        new WebAssembly.Table({initial: 20, maximum: 20, element: "anyfunc"});
+        new WebAssembly.Table({initial: 20, maximum: 25, element: "anyfunc"});
         break;
     case "CompileError":
     case "RuntimeError": {
index 50592c9..daa4e1a 100644 (file)
@@ -1,3 +1,96 @@
+2016-12-13  Saam Barati  <sbarati@apple.com>
+
+        WebAssembly: implement the table section and table import
+        https://bugs.webkit.org/show_bug.cgi?id=165716
+
+        Reviewed by Keith Miller.
+
+        This patch implements the Table space for wasm:
+        https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#table-section
+
+        It only implements defining and importing a table. The bulk
+        of this patch is implementing the various wasm Table prototype
+        methods and the underlying Table object:
+        https://github.com/WebAssembly/design/blob/master/JS.md#webassemblytable-constructor
+
+        This patch also fixes a bug in our implementation with call_indirect.
+        We initially implemented call_indirect as a way to call functions that
+        are imported or defined in the module. This was the wrong
+        interpretation of the spec. Instead, call_indirect can only index into
+        the table index space.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::B3IRGenerator):
+        (JSC::Wasm::B3IRGenerator::addCallIndirect):
+        (JSC::Wasm::parseAndCompile):
+        * wasm/WasmFormat.h:
+        (JSC::Wasm::TableInformation::TableInformation):
+        (JSC::Wasm::TableInformation::operator bool):
+        (JSC::Wasm::TableInformation::isImport):
+        (JSC::Wasm::TableInformation::initial):
+        (JSC::Wasm::TableInformation::maximum):
+        (JSC::Wasm::CallableFunction::CallableFunction):
+        * wasm/WasmFunctionParser.h:
+        (JSC::Wasm::FunctionParser<Context>::parseExpression):
+        * wasm/WasmModuleParser.cpp:
+        (JSC::Wasm::ModuleParser::parseImport):
+        (JSC::Wasm::ModuleParser::parseResizableLimits):
+        (JSC::Wasm::ModuleParser::parseTableHelper):
+        (JSC::Wasm::ModuleParser::parseTable):
+        (JSC::Wasm::ModuleParser::parseMemoryHelper):
+        (JSC::Wasm::ModuleParser::parseExport):
+        * wasm/WasmModuleParser.h:
+        * wasm/js/JSWebAssemblyHelpers.h: Added.
+        (JSC::toNonWrappingUint32):
+        * wasm/js/JSWebAssemblyInstance.cpp:
+        (JSC::JSWebAssemblyInstance::visitChildren):
+        * wasm/js/JSWebAssemblyInstance.h:
+        (JSC::JSWebAssemblyInstance::table):
+        (JSC::JSWebAssemblyInstance::setTable):
+        (JSC::JSWebAssemblyInstance::offsetOfTable):
+        * wasm/js/JSWebAssemblyTable.cpp:
+        (JSC::JSWebAssemblyTable::create):
+        (JSC::JSWebAssemblyTable::JSWebAssemblyTable):
+        (JSC::JSWebAssemblyTable::visitChildren):
+        (JSC::JSWebAssemblyTable::grow):
+        (JSC::JSWebAssemblyTable::clearFunction):
+        (JSC::JSWebAssemblyTable::setFunction):
+        * wasm/js/JSWebAssemblyTable.h:
+        (JSC::JSWebAssemblyTable::maximum):
+        (JSC::JSWebAssemblyTable::size):
+        (JSC::JSWebAssemblyTable::getFunction):
+        (JSC::JSWebAssemblyTable::offsetOfSize):
+        (JSC::JSWebAssemblyTable::offsetOfFunctions):
+        (JSC::JSWebAssemblyTable::isValidSize):
+        * wasm/js/WebAssemblyFunction.cpp:
+        (JSC::WebAssemblyFunction::call):
+        (JSC::WebAssemblyFunction::create):
+        (JSC::WebAssemblyFunction::visitChildren):
+        (JSC::WebAssemblyFunction::finishCreation):
+        * wasm/js/WebAssemblyFunction.h:
+        (JSC::WebAssemblyFunction::signature):
+        (JSC::WebAssemblyFunction::wasmEntrypoint):
+        (JSC::WebAssemblyFunction::webAssemblyCallee): Deleted.
+        * wasm/js/WebAssemblyInstanceConstructor.cpp:
+        (JSC::constructJSWebAssemblyInstance):
+        * wasm/js/WebAssemblyMemoryConstructor.cpp:
+        (JSC::constructJSWebAssemblyMemory):
+        * wasm/js/WebAssemblyModuleRecord.cpp:
+        (JSC::WebAssemblyModuleRecord::finishCreation):
+        (JSC::WebAssemblyModuleRecord::link):
+        * wasm/js/WebAssemblyTableConstructor.cpp:
+        (JSC::constructJSWebAssemblyTable):
+        * wasm/js/WebAssemblyTablePrototype.cpp:
+        (JSC::getTable):
+        (JSC::webAssemblyTableProtoFuncLength):
+        (JSC::webAssemblyTableProtoFuncGrow):
+        (JSC::webAssemblyTableProtoFuncGet):
+        (JSC::webAssemblyTableProtoFuncSet):
+        (JSC::WebAssemblyTablePrototype::create):
+        (JSC::WebAssemblyTablePrototype::finishCreation):
+        * wasm/js/WebAssemblyTablePrototype.h:
+
 2016-12-13  Filip Pizlo  <fpizlo@apple.com>
 
         Add null checks to opaque root APIs.
index 44146b1..0c2443a 100644 (file)
                795B19971D78BE3500262FA0 /* MapBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 795B19951D78BE3500262FA0 /* MapBase.cpp */; };
                795B19981D78BE3500262FA0 /* MapBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 795B19961D78BE3500262FA0 /* MapBase.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7964656A1B952FF0003059EE /* GetPutInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 796465681B952FF0003059EE /* GetPutInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               796FB43A1DFF8C3F0039C95D /* JSWebAssemblyHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 796FB4391DFF8C3F0039C95D /* JSWebAssemblyHelpers.h */; settings = {ATTRIBUTES = (Private, ); }; };
                797E07A91B8FCFB9008400BA /* JSGlobalLexicalEnvironment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 797E07A71B8FCFB9008400BA /* JSGlobalLexicalEnvironment.cpp */; };
                797E07AA1B8FCFB9008400BA /* JSGlobalLexicalEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = 797E07A81B8FCFB9008400BA /* JSGlobalLexicalEnvironment.h */; settings = {ATTRIBUTES = (Private, ); }; };
                798937781DCAB57300F8D4FB /* JSFixedArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 798937761DCAB57300F8D4FB /* JSFixedArray.cpp */; };
                795B19951D78BE3500262FA0 /* MapBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MapBase.cpp; sourceTree = "<group>"; };
                795B19961D78BE3500262FA0 /* MapBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapBase.h; sourceTree = "<group>"; };
                796465681B952FF0003059EE /* GetPutInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetPutInfo.h; sourceTree = "<group>"; };
+               796FB4391DFF8C3F0039C95D /* JSWebAssemblyHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JSWebAssemblyHelpers.h; path = js/JSWebAssemblyHelpers.h; sourceTree = "<group>"; };
                797E07A71B8FCFB9008400BA /* JSGlobalLexicalEnvironment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSGlobalLexicalEnvironment.cpp; sourceTree = "<group>"; };
                797E07A81B8FCFB9008400BA /* JSGlobalLexicalEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGlobalLexicalEnvironment.h; sourceTree = "<group>"; };
                798937761DCAB57300F8D4FB /* JSFixedArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFixedArray.cpp; sourceTree = "<group>"; };
                                79E423E11DEE65320078D355 /* JSWebAssemblyCallee.h */,
                                AD2FCBA61DB58DA400B3E736 /* JSWebAssemblyCompileError.cpp */,
                                AD2FCBA71DB58DA400B3E736 /* JSWebAssemblyCompileError.h */,
+                               796FB4391DFF8C3F0039C95D /* JSWebAssemblyHelpers.h */,
                                AD2FCBA81DB58DA400B3E736 /* JSWebAssemblyInstance.cpp */,
                                AD2FCBA91DB58DA400B3E736 /* JSWebAssemblyInstance.h */,
                                AD2FCBAA1DB58DA400B3E736 /* JSWebAssemblyMemory.cpp */,
                                43422A671C16267800E2EB98 /* B3ReduceDoubleToFloat.h in Headers */,
                                0F070A4B1D543A98006E7232 /* LargeAllocation.h in Headers */,
                                86D3B2C610156BDE002865E7 /* MacroAssemblerARM.h in Headers */,
+                               796FB43A1DFF8C3F0039C95D /* JSWebAssemblyHelpers.h in Headers */,
                                A1A009C01831A22D00CF8711 /* MacroAssemblerARM64.h in Headers */,
                                86ADD1460FDDEA980006EEC2 /* MacroAssemblerARMv7.h in Headers */,
                                863B23E00FC6118900703AA4 /* MacroAssemblerCodeRef.h in Headers */,
index a6c11bd..790e0b1 100644 (file)
@@ -137,7 +137,7 @@ public:
 
     static constexpr ExpressionType emptyExpression = nullptr;
 
-    B3IRGenerator(const MemoryInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&, const ImmutableFunctionIndexSpace&);
+    B3IRGenerator(VM&, const MemoryInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&, const ImmutableFunctionIndexSpace&);
 
     bool WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
     bool WARN_UNUSED_RETURN addLocal(Type, uint32_t);
@@ -188,6 +188,7 @@ private:
     void unifyValuesWithBlock(const ExpressionList& resultStack, ResultList& stack);
     Value* zeroForType(Type);
 
+    VM& m_vm;
     const ImmutableFunctionIndexSpace& m_functionIndexSpace;
     Procedure& m_proc;
     BasicBlock* m_currentBlock;
@@ -199,8 +200,9 @@ private:
     Value* m_functionIndexSpaceValue;
 };
 
-B3IRGenerator::B3IRGenerator(const MemoryInformation& memory, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ImmutableFunctionIndexSpace& functionIndexSpace)
-    : m_functionIndexSpace(functionIndexSpace)
+B3IRGenerator::B3IRGenerator(VM& vm, const MemoryInformation& memory, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ImmutableFunctionIndexSpace& functionIndexSpace)
+    : m_vm(vm)
+    , m_functionIndexSpace(functionIndexSpace)
     , m_proc(procedure)
     , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls)
 {
@@ -669,27 +671,50 @@ bool B3IRGenerator::addCallIndirect(const Signature* signature, Vector<Expressio
     ExpressionType calleeIndex = args.takeLast();
     ASSERT(signature->arguments.size() == args.size());
 
+    ExpressionType callableFunctionBuffer;
+    ExpressionType callableFunctionBufferSize;
+    {
+        ExpressionType topInstance = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(),
+            m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), &m_vm.topJSWebAssemblyInstance));
+        ExpressionType table = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(),
+            topInstance, JSWebAssemblyInstance::offsetOfTable());
+        callableFunctionBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(),
+            table, JSWebAssemblyTable::offsetOfFunctions());
+        callableFunctionBufferSize = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, Origin(),
+            table, JSWebAssemblyTable::offsetOfSize());
+    }
+
     // Check the index we are looking for is valid.
     {
-        ExpressionType maxValidIndex = m_currentBlock->appendIntConstant(m_proc, Origin(), Int32, m_functionIndexSpace.size);
         CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(),
-            m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), m_zeroValues[linearizeType(I32)],
-                m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), calleeIndex, maxValidIndex)));
+            m_currentBlock->appendNew<Value>(m_proc, AboveEqual, Origin(), calleeIndex, callableFunctionBufferSize));
 
         check->setGenerator([] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
             jit.breakpoint();
         });
     }
 
-    // Compute the offset in the function index space we are looking for.
+    // Compute the offset in the table index space we are looking for.
     ExpressionType offset = m_currentBlock->appendNew<Value>(m_proc, Mul, Origin(),
         m_currentBlock->appendNew<Value>(m_proc, ZExt32, Origin(), calleeIndex),
         m_currentBlock->appendIntConstant(m_proc, Origin(), pointerType(), sizeof(CallableFunction)));
-    ExpressionType callableFunction = m_currentBlock->appendNew<Value>(m_proc, Add, Origin(), m_functionIndexSpaceValue, offset);
+    ExpressionType callableFunction = m_currentBlock->appendNew<Value>(m_proc, Add, Origin(), callableFunctionBuffer, offset);
+
+    // Check that the CallableFunction is initialized. We trap if it isn't. A null Signature* indicates it's not initialized.
+    ExpressionType calleeSignature = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), callableFunction, OBJECT_OFFSETOF(CallableFunction, signature));
+    {
+        CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(),
+            m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), 
+                calleeSignature, 
+                m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), 0)));
+
+        check->setGenerator([] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
+            jit.breakpoint();
+        });
+    }
 
     // Check the signature matches the value we expect.
     {
-        ExpressionType calleeSignature = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), callableFunction, OBJECT_OFFSETOF(CallableFunction, signature));
         ExpressionType expectedSignature = m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), signature);
         CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(),
             m_currentBlock->appendNew<Value>(m_proc, NotEqual, Origin(), calleeSignature, expectedSignature));
@@ -713,6 +738,7 @@ bool B3IRGenerator::addCallIndirect(const Signature* signature, Vector<Expressio
                 jit.call(params[returnType == Void ? 0 : 1].gpr());
             });
         });
+
     return true;
 }
 
@@ -846,7 +872,7 @@ std::unique_ptr<WasmInternalFunction> parseAndCompile(VM& vm, const uint8_t* fun
     auto result = std::make_unique<WasmInternalFunction>();
 
     Procedure procedure;
-    B3IRGenerator context(info.memory, procedure, result.get(), unlinkedWasmToWasmCalls, functionIndexSpace);
+    B3IRGenerator context(vm, info.memory, procedure, result.get(), unlinkedWasmToWasmCalls, functionIndexSpace);
     FunctionParser<B3IRGenerator> parser(context, functionStart, functionLength, signature, functionIndexSpace, info);
     if (!parser.parse())
         RELEASE_ASSERT_NOT_REACHED();
index d992dba..8589585 100644 (file)
@@ -106,7 +106,7 @@ struct Export {
     External::Kind kind;
     union {
         uint32_t functionIndex;
-        // FIXME implement Table https://bugs.webkit.org/show_bug.cgi?id=164135
+        // FIXME implement Table https://bugs.webkit.org/show_bug.cgi?id=165782
         // FIXME implement Memory https://bugs.webkit.org/show_bug.cgi?id=165671
         // FIXME implement Global https://bugs.webkit.org/show_bug.cgi?id=164133
     };
@@ -147,17 +147,45 @@ struct Segment {
     }
 };
 
+class TableInformation {
+public:
+    TableInformation()
+    {
+        ASSERT(!*this);
+    }
+
+    TableInformation(uint32_t initial, std::optional<uint32_t> maximum, bool isImport)
+        : m_initial(initial)
+        , m_maximum(maximum)
+        , m_isImport(isImport)
+        , m_isValid(true)
+    {
+        ASSERT(*this);
+    }
+
+    explicit operator bool() const { return m_isValid; }
+    bool isImport() const { return m_isImport; }
+    uint32_t initial() const { return m_initial; }
+    std::optional<uint32_t> maximum() const { return m_maximum; }
+
+private:
+    uint32_t m_initial;
+    std::optional<uint32_t> m_maximum;
+    bool m_isImport { false };
+    bool m_isValid { false };
+};
+
 struct ModuleInformation {
     Vector<Signature> signatures;
     Vector<Import> imports;
     Vector<Signature*> importFunctions;
-    // FIXME implement import Table https://bugs.webkit.org/show_bug.cgi?id=164135
     // FIXME implement import Global https://bugs.webkit.org/show_bug.cgi?id=164133
     Vector<Signature*> internalFunctionSignatures;
     MemoryInformation memory;
     Vector<Export> exports;
     std::optional<uint32_t> startFunctionIndexSpace;
     Vector<Segment::Ptr> data;
+    TableInformation tableInformation;
 
     ~ModuleInformation();
 };
@@ -185,13 +213,18 @@ typedef MacroAssemblerCodeRef WasmToJSStub;
 // WebAssembly direct calls and call_indirect use indices into "function index space". This space starts with all imports, and then all internal functions.
 // CallableFunction and FunctionIndexSpace are only meant as fast lookup tables for these opcodes, and do not own code.
 struct CallableFunction {
-    CallableFunction(Signature* signature)
+    CallableFunction() = default;
+
+    CallableFunction(Signature* signature, void* code = nullptr)
         : signature(signature)
-        , code(nullptr)
+        , code(code)
     {
     }
-    Signature* signature; // FIXME pack this inside a (uniqued) integer (for correctness the parser should unique Signatures), and then pack that integer into the code pointer. https://bugs.webkit.org/show_bug.cgi?id=165511
-    void* code;
+
+    // FIXME pack this inside a (uniqued) integer (for correctness the parser should unique Signatures),
+    // and then pack that integer into the code pointer. https://bugs.webkit.org/show_bug.cgi?id=165511
+    Signature* signature { nullptr }; 
+    void* code { nullptr };
 };
 typedef Vector<CallableFunction> FunctionIndexSpace;
 
index 207018b..88283ba 100644 (file)
@@ -382,6 +382,8 @@ bool FunctionParser<Context>::parseExpression(OpType op)
     }
 
     case OpType::CallIndirect: {
+        if (!m_info.tableInformation)
+            return setErrorMessage("call_indirect is only valid when a table is defined or imported");
         uint32_t signatureIndex;
         if (!parseVarUInt32(signatureIndex))
             return false;
@@ -390,6 +392,9 @@ bool FunctionParser<Context>::parseExpression(OpType op)
         if (!parseVarUInt1(reserved))
             return false;
 
+        if (reserved != 0)
+            return setErrorMessage("call_indirect 'reserved' varuint1 must be 0x0");
+
         if (m_info.signatures.size() <= signatureIndex)
             return setErrorMessage("Tried to use a signature outside the range of valid signatures");
 
index 4e5d00d..f92679c 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(WEBASSEMBLY)
 
 #include "IdentifierInlines.h"
+#include "JSWebAssemblyTable.h"
 #include "WasmFormat.h"
 #include "WasmMemoryInformation.h"
 #include "WasmOps.h"
@@ -250,7 +251,9 @@ bool ModuleParser::parseImport()
             break;
         }
         case External::Table: {
-            // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
+            bool isImport = true;
+            if (!parseTableHelper(isImport))
+                return false;
             break;
         }
         case External::Memory: {
@@ -300,45 +303,101 @@ bool ModuleParser::parseFunction()
     return true;
 }
 
-bool ModuleParser::parseTable()
+bool ModuleParser::parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum)
 {
-    // FIXME implement table https://bugs.webkit.org/show_bug.cgi?id=164135
-    RELEASE_ASSERT_NOT_REACHED();
+    ASSERT(!maximum);
+
+    uint8_t flags;
+    if (!parseVarUInt1(flags))
+        return false;
+
+    if (!parseVarUInt32(initial))
+        return false;
+
+    if (flags) {
+        uint32_t maximumInt;
+        if (!parseVarUInt32(maximumInt))
+            return false;
+
+        if (initial > maximumInt)
+            return false;
+
+        maximum = maximumInt;
+    }
+
     return true;
 }
 
-bool ModuleParser::parseMemoryHelper(bool isImport)
+bool ModuleParser::parseTableHelper(bool isImport)
 {
-    // We don't allow redeclaring memory. Either via import or definition.
-    if (m_module->memory)
+    // We're only allowed a total of one Table import or definition.
+    if (m_hasTable)
         return false;
 
-    uint8_t flags;
-    if (!parseVarUInt1(flags))
+    m_hasTable = true;
+
+    int8_t type;
+    if (!parseInt7(type))
+        return false;
+    if (type != Wasm::Anyfunc)
         return false;
 
     uint32_t initial;
-    if (!parseVarUInt32(initial))
+    std::optional<uint32_t> maximum;
+    if (!parseResizableLimits(initial, maximum))
         return false;
 
-    if (!PageCount::isValid(initial))
+    if (!JSWebAssemblyTable::isValidSize(initial))
         return false;
 
-    PageCount initialPageCount(initial);
+    ASSERT(!maximum || *maximum >= initial);
 
+    m_module->tableInformation = TableInformation(initial, maximum, isImport);
+
+    return true;
+}
+
+bool ModuleParser::parseTable()
+{
+    uint32_t count;
+    if (!parseVarUInt32(count))
+        return false;
+
+    // We only allow one table for now.
+    if (count != 1)
+        return false;
+
+    bool isImport = false;
+    return parseTableHelper(isImport);
+}
+
+bool ModuleParser::parseMemoryHelper(bool isImport)
+{
+    // We don't allow redeclaring memory. Either via import or definition.
+    if (m_module->memory)
+        return false;
+
+    PageCount initialPageCount;
     PageCount maximumPageCount;
-    if (flags) {
-        uint32_t maximum;
-        if (!parseVarUInt32(maximum))
+    {
+        uint32_t initial;
+        std::optional<uint32_t> maximum;
+        if (!parseResizableLimits(initial, maximum))
             return false;
-
-        if (!PageCount::isValid(maximum))
+        ASSERT(!maximum || *maximum >= initial);
+        if (!PageCount::isValid(initial))
             return false;
 
-        maximumPageCount = PageCount(maximum);
-        if (initialPageCount > maximumPageCount)
-            return false;
+        initialPageCount = PageCount(initial);
+
+        if (maximum) {
+            if (!PageCount::isValid(*maximum))
+                return false;
+            maximumPageCount = PageCount(*maximum);
+        }
     }
+    ASSERT(initialPageCount);
+    ASSERT(!maximumPageCount || maximumPageCount >= initialPageCount);
 
     Vector<unsigned> pinnedSizes = { 0 };
     m_module->memory = MemoryInformation(initialPageCount, maximumPageCount, pinnedSizes, isImport);
@@ -397,7 +456,7 @@ bool ModuleParser::parseExport()
             break;
         }
         case External::Table: {
-            // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
+            // FIXME https://bugs.webkit.org/show_bug.cgi?id=165782
             break;
         }
         case External::Memory: {
index 3007f67..4d1726e 100644 (file)
@@ -79,12 +79,15 @@ private:
 #undef WASM_SECTION_DECLARE_PARSER
 
     bool WARN_UNUSED_RETURN parseMemoryHelper(bool isImport);
+    bool WARN_UNUSED_RETURN parseTableHelper(bool isImport);
+    bool WARN_UNUSED_RETURN parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum);
 
     VM* m_vm;
     std::unique_ptr<ModuleInformation> m_module;
     FunctionIndexSpace m_functionIndexSpace;
     Vector<FunctionLocationInBinary> m_functionLocationInBinary;
     bool m_failed { true };
+    bool m_hasTable { false };
     String m_errorMessage;
 };
 
diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyHelpers.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyHelpers.h
new file mode 100644 (file)
index 0000000..2527878
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "JSCJSValue.h"
+
+namespace JSC {
+
+ALWAYS_INLINE uint32_t toNonWrappingUint32(ExecState* exec, JSValue value)
+{
+    VM& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+    double doubleValue = value.toInteger(exec);
+    RETURN_IF_EXCEPTION(throwScope, { });
+    if (doubleValue < 0 || doubleValue > UINT_MAX) {
+        throwException(exec, throwScope,
+            createRangeError(exec, ASCIILiteral("Expect an integer argument in the range: [0, 2^32 - 1]")));
+        return { };
+    }
+
+    return static_cast<uint32_t>(doubleValue);
+}
+
+} // namespace JSC
+
+#endif // ENABLE(WEBASSEMBLY)
index 3dbd1b6..8e22b07 100644 (file)
@@ -80,6 +80,7 @@ void JSWebAssemblyInstance::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(&thisObject->m_module);
     visitor.append(&thisObject->m_moduleNamespaceObject);
     visitor.append(&thisObject->m_memory);
+    visitor.append(&thisObject->m_table);
     for (unsigned i = 0; i < thisObject->m_numImportFunctions; ++i)
         visitor.append(thisObject->importFunction(i));
 }
index 6cb5a71..30cf350 100644 (file)
@@ -30,6 +30,7 @@
 #include "JSDestructibleObject.h"
 #include "JSObject.h"
 #include "JSWebAssemblyMemory.h"
+#include "JSWebAssemblyTable.h"
 
 namespace JSC {
 
@@ -71,11 +72,16 @@ public:
     JSWebAssemblyMemory* memory() { return m_memory.get(); }
     void setMemory(VM& vm, JSWebAssemblyMemory* memory) { m_memory.set(vm, this, memory); }
 
+    JSWebAssemblyTable* table() { return m_table.get(); }
+    void setTable(VM& vm, JSWebAssemblyTable* table) { m_table.set(vm, this, table); }
+
     static size_t offsetOfImportFunction(unsigned idx)
     {
         return offsetOfImportFunctions() + sizeof(WriteBarrier<JSCell>) * idx;
     }
 
+    static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_table); }
+
 protected:
     JSWebAssemblyInstance(VM&, Structure*, unsigned);
     void finishCreation(VM&, JSWebAssemblyModule*, JSModuleNamespaceObject*);
@@ -96,6 +102,7 @@ private:
     WriteBarrier<JSWebAssemblyModule> m_module;
     WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
     WriteBarrier<JSWebAssemblyMemory> m_memory;
+    WriteBarrier<JSWebAssemblyTable> m_table;
     unsigned m_numImportFunctions;
 };
 
index cb5e918..aee5417 100644 (file)
 #if ENABLE(WEBASSEMBLY)
 
 #include "JSCInlines.h"
+#include "WasmFormat.h"
 
 namespace JSC {
 
-JSWebAssemblyTable* JSWebAssemblyTable::create(VM& vm, Structure* structure)
+const ClassInfo JSWebAssemblyTable::s_info = { "WebAssembly.Table", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyTable) };
+
+JSWebAssemblyTable* JSWebAssemblyTable::create(ExecState* exec, VM& vm, Structure* structure, uint32_t initial, std::optional<uint32_t> maximum)
 {
-    auto* instance = new (NotNull, allocateCell<JSWebAssemblyTable>(vm.heap)) JSWebAssemblyTable(vm, structure);
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+    if (!isValidSize(initial)) {
+        throwException(exec, throwScope, createOutOfMemoryError(exec));
+        return nullptr;
+    }
+
+    auto* instance = new (NotNull, allocateCell<JSWebAssemblyTable>(vm.heap)) JSWebAssemblyTable(vm, structure, initial, maximum);
     instance->finishCreation(vm);
     return instance;
 }
@@ -44,9 +53,23 @@ Structure* JSWebAssemblyTable::createStructure(VM& vm, JSGlobalObject* globalObj
     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
 }
 
-JSWebAssemblyTable::JSWebAssemblyTable(VM& vm, Structure* structure)
+JSWebAssemblyTable::JSWebAssemblyTable(VM& vm, Structure* structure, uint32_t initial, std::optional<uint32_t> maximum)
     : Base(vm, structure)
 {
+    m_size = initial;
+    ASSERT(isValidSize(m_size));
+    m_maximum = maximum;
+    ASSERT(!m_maximum || *m_maximum >= m_size);
+
+    // FIXME: It might be worth trying to pre-allocate maximum here. The spec recommends doing so.
+    // But for now, we're not doing that.
+    m_functions = MallocPtr<Wasm::CallableFunction>::malloc(sizeof(Wasm::CallableFunction) * static_cast<size_t>(m_size));
+    m_jsFunctions = MallocPtr<WriteBarrier<WebAssemblyFunction>>::malloc(sizeof(WriteBarrier<WebAssemblyFunction>) * static_cast<size_t>(m_size));
+    for (uint32_t i = 0; i < m_size; ++i) {
+        new (&m_functions.get()[i]) Wasm::CallableFunction();
+        ASSERT(!m_functions.get()[i].signature); // We rely on this in compiled code.
+        new (&m_jsFunctions.get()[i]) WriteBarrier<WebAssemblyFunction>();
+    }
 }
 
 void JSWebAssemblyTable::finishCreation(VM& vm)
@@ -62,13 +85,51 @@ void JSWebAssemblyTable::destroy(JSCell* cell)
 
 void JSWebAssemblyTable::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
-    auto* thisObject = jsCast<JSWebAssemblyTable*>(cell);
+    JSWebAssemblyTable* thisObject = jsCast<JSWebAssemblyTable*>(cell);
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
 
     Base::visitChildren(thisObject, visitor);
+
+    for (unsigned i = 0; i < thisObject->m_size; ++i)
+        visitor.append(&thisObject->m_jsFunctions.get()[i]);
 }
 
-const ClassInfo JSWebAssemblyTable::s_info = { "WebAssembly.Table", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyTable) };
+bool JSWebAssemblyTable::grow(uint32_t newSize)
+{
+    if (newSize < m_size)
+        return false;
+    if (newSize == m_size)
+        return true;
+    if (maximum() && newSize > *maximum())
+        return false;
+    if (!isValidSize(newSize))
+        return false;
+
+    m_functions.realloc(sizeof(Wasm::CallableFunction) * static_cast<size_t>(newSize));
+    m_jsFunctions.realloc(sizeof(WriteBarrier<WebAssemblyFunction>) * static_cast<size_t>(newSize));
+
+    for (uint32_t i = m_size; i < newSize; ++i) {
+        new (&m_functions.get()[i]) Wasm::CallableFunction();
+        new (&m_jsFunctions.get()[i]) WriteBarrier<WebAssemblyFunction>();
+    }
+    m_size = newSize;
+    return true;
+}
+
+void JSWebAssemblyTable::clearFunction(uint32_t index)
+{
+    RELEASE_ASSERT(index < m_size);
+    m_jsFunctions.get()[index] = WriteBarrier<WebAssemblyFunction>();
+    m_functions.get()[index] = Wasm::CallableFunction();
+    ASSERT(!m_functions.get()[index].signature); // We rely on this in compiled code.
+}
+
+void JSWebAssemblyTable::setFunction(VM& vm, uint32_t index, WebAssemblyFunction* function)
+{
+    RELEASE_ASSERT(index < m_size);
+    m_jsFunctions.get()[index].set(vm, this, function);
+    m_functions.get()[index] = Wasm::CallableFunction(function->signature(), function->wasmEntrypoint());
+}
 
 } // namespace JSC
 
index 01e68dd..468a027 100644 (file)
 
 #include "JSDestructibleObject.h"
 #include "JSObject.h"
+#include "WebAssemblyFunction.h"
+#include <wtf/MallocPtr.h>
 
 namespace JSC {
 
+namespace Wasm {
+struct CallableFunction;
+}
+
 class JSWebAssemblyTable : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static JSWebAssemblyTable* create(VM&, Structure*);
+    static JSWebAssemblyTable* create(ExecState*, VM&, Structure*, uint32_t initial, std::optional<uint32_t> maximum);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
 
-protected:
-    JSWebAssemblyTable(VM&, Structure*);
+    std::optional<uint32_t> maximum() const { return m_maximum; }
+    uint32_t size() const { return m_size; }
+    bool grow(uint32_t newSize) WARN_UNUSED_RETURN;
+    WebAssemblyFunction* getFunction(uint32_t index)
+    {
+        RELEASE_ASSERT(index < m_size);
+        return m_jsFunctions.get()[index].get();
+    }
+    void clearFunction(uint32_t index);
+    void setFunction(VM&, uint32_t index, WebAssemblyFunction*);
+
+    static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(JSWebAssemblyTable, m_size); }
+    static ptrdiff_t offsetOfFunctions() { return OBJECT_OFFSETOF(JSWebAssemblyTable, m_functions); }
+
+    static bool isValidSize(uint32_t size)
+    {
+        // This tops out at ~384 MB worth of data in this class.
+        return size < (1 << 24);
+    }
+
+private:
+    JSWebAssemblyTable(VM&, Structure*, uint32_t initial, std::optional<uint32_t> maximum);
     void finishCreation(VM&);
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
+
+    MallocPtr<Wasm::CallableFunction> m_functions;
+    MallocPtr<WriteBarrier<WebAssemblyFunction>> m_jsFunctions;
+    std::optional<uint32_t> m_maximum;
+    uint32_t m_size;
 };
 
 } // namespace JSC
index a55f960..f0be5f3 100644 (file)
@@ -117,7 +117,7 @@ EncodedJSValue WebAssemblyFunction::call(VM& vm, ProtoCallFrame* protoCallFrame)
     JSWebAssemblyInstance* prevJSWebAssemblyInstance = vm.topJSWebAssemblyInstance;
     vm.topJSWebAssemblyInstance = instance();
     ASSERT(instance());
-    EncodedJSValue rawResult = vmEntryToWasm(webAssemblyCallee()->entrypoint(), &vm, protoCallFrame);
+    EncodedJSValue rawResult = vmEntryToWasm(m_jsEntrypoint->entrypoint(), &vm, protoCallFrame);
     vm.topJSWebAssemblyInstance = prevJSWebAssemblyInstance;
 
     // FIXME is this correct? https://bugs.webkit.org/show_bug.cgi?id=164876
@@ -140,12 +140,12 @@ EncodedJSValue WebAssemblyFunction::call(VM& vm, ProtoCallFrame* protoCallFrame)
     return EncodedJSValue();
 }
 
-WebAssemblyFunction* WebAssemblyFunction::create(VM& vm, JSGlobalObject* globalObject, unsigned length, const String& name, JSWebAssemblyInstance* instance, JSWebAssemblyCallee* callee, Wasm::Signature* signature)
+WebAssemblyFunction* WebAssemblyFunction::create(VM& vm, JSGlobalObject* globalObject, unsigned length, const String& name, JSWebAssemblyInstance* instance, JSWebAssemblyCallee* jsEntrypoint, JSWebAssemblyCallee* wasmEntrypoint, Wasm::Signature* signature)
 {
     NativeExecutable* executable = vm.getHostFunction(callWebAssemblyFunction, NoIntrinsic, callHostFunctionAsConstructor, nullptr, name);
     Structure* structure = globalObject->webAssemblyFunctionStructure();
     WebAssemblyFunction* function = new (NotNull, allocateCell<WebAssemblyFunction>(vm.heap)) WebAssemblyFunction(vm, globalObject, structure);
-    function->finishCreation(vm, executable, length, name, instance, callee, signature);
+    function->finishCreation(vm, executable, length, name, instance, jsEntrypoint, wasmEntrypoint, signature);
     return function;
 }
 
@@ -166,15 +166,18 @@ void WebAssemblyFunction::visitChildren(JSCell* cell, SlotVisitor& visitor)
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     Base::visitChildren(thisObject, visitor);
     visitor.append(&thisObject->m_instance);
-    visitor.append(&thisObject->m_wasmCallee);
+    visitor.append(&thisObject->m_jsEntrypoint);
+    visitor.append(&thisObject->m_wasmEntrypoint);
 }
 
-void WebAssemblyFunction::finishCreation(VM& vm, NativeExecutable* executable, unsigned length, const String& name, JSWebAssemblyInstance* instance, JSWebAssemblyCallee* wasmCallee, Wasm::Signature* signature)
+void WebAssemblyFunction::finishCreation(VM& vm, NativeExecutable* executable, unsigned length, const String& name, JSWebAssemblyInstance* instance, JSWebAssemblyCallee* jsEntrypoint, JSWebAssemblyCallee* wasmEntrypoint, Wasm::Signature* signature)
 {
     Base::finishCreation(vm, executable, length, name);
     ASSERT(inherits(info()));
     m_instance.set(vm, this, instance);
-    m_wasmCallee.set(vm, this, wasmCallee);
+    ASSERT(jsEntrypoint != wasmEntrypoint);
+    m_jsEntrypoint.set(vm, this, jsEntrypoint);
+    m_wasmEntrypoint.set(vm, this, wasmEntrypoint);
     m_signature = signature;
 }
 
index 2cbf852..70d1a65 100644 (file)
 #if ENABLE(WEBASSEMBLY)
 
 #include "JSFunction.h"
+#include "JSWebAssemblyCallee.h"
 #include <wtf/Noncopyable.h>
 
 namespace JSC {
 
 class JSGlobalObject;
-class JSWebAssemblyCallee;
 struct ProtoCallFrame;
 class WebAssemblyInstance;
 
@@ -53,28 +53,29 @@ public:
 
     DECLARE_EXPORT_INFO;
 
-    JS_EXPORT_PRIVATE static WebAssemblyFunction* create(VM&, JSGlobalObject*, unsigned, const String&, JSWebAssemblyInstance*, JSWebAssemblyCallee*, Wasm::Signature*);
+    JS_EXPORT_PRIVATE static WebAssemblyFunction* create(VM&, JSGlobalObject*, unsigned, const String&, JSWebAssemblyInstance*, JSWebAssemblyCallee* jsEntrypoint, JSWebAssemblyCallee* wasmEntrypoint, Wasm::Signature*);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
-    JSWebAssemblyCallee* webAssemblyCallee() const { return m_wasmCallee.get(); }
     JSWebAssemblyInstance* instance() const { return m_instance.get(); }
-    const Wasm::Signature* signature()
+    Wasm::Signature* signature()
     { 
         ASSERT(m_signature);
         return m_signature;
     }
     EncodedJSValue call(VM&, ProtoCallFrame*);
+    void* wasmEntrypoint() { return m_wasmEntrypoint->entrypoint(); }
 
 protected:
     static void visitChildren(JSCell*, SlotVisitor&);
 
-    void finishCreation(VM&, NativeExecutable*, unsigned length, const String& name, JSWebAssemblyInstance*, JSWebAssemblyCallee*, Wasm::Signature*);
+    void finishCreation(VM&, NativeExecutable*, unsigned length, const String& name, JSWebAssemblyInstance*, JSWebAssemblyCallee* jsEntrypoint, JSWebAssemblyCallee* wasmEntrypoint, Wasm::Signature*);
 
 private:
     WebAssemblyFunction(VM&, JSGlobalObject*, Structure*);
 
     WriteBarrier<JSWebAssemblyInstance> m_instance;
-    WriteBarrier<JSWebAssemblyCallee> m_wasmCallee;
+    WriteBarrier<JSWebAssemblyCallee> m_jsEntrypoint;
+    WriteBarrier<JSWebAssemblyCallee> m_wasmEntrypoint;
     Wasm::Signature* m_signature;
 };
 
index bf0aac2..b5c3b61 100644 (file)
@@ -88,10 +88,10 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
     // Let imports be an initially-empty list of external values.
     unsigned numImportFunctions = 0;
 
-    // FIXME implement Table https://bugs.webkit.org/show_bug.cgi?id=164135
     // FIXME implement Global https://bugs.webkit.org/show_bug.cgi?id=164133
 
     bool hasMemoryImport = false;
+    bool hasTableImport = false;
     // For each import i in module.imports:
     for (auto& import : moduleInformation.imports) {
         // 1. Let o be the resultant value of performing Get(importObject, i.module_name).
@@ -131,12 +131,34 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
             break;
         }
         case Wasm::External::Table: {
+            RELEASE_ASSERT(!hasTableImport); // This should be guaranteed by a validation failure.
             // 7. Otherwise (i is a table import):
-            // FIXME implement Table https://bugs.webkit.org/show_bug.cgi?id=164135
+            hasTableImport = true;
+            JSWebAssemblyTable* table = jsDynamicCast<JSWebAssemblyTable*>(value);
             // i. If v is not a WebAssembly.Table object, throw a TypeError.
+            if (!table)
+                return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("Table import is not an instance of WebAssembly.Table"))));
+
+            uint32_t expectedInitial = moduleInformation.tableInformation.initial();
+            uint32_t actualInitial = table->size();
+            if (actualInitial < expectedInitial)
+                return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("Table import provided an 'initial' that is too small"))));
+
+            if (std::optional<uint32_t> expectedMaximum = moduleInformation.tableInformation.maximum()) {
+                std::optional<uint32_t> actualMaximum = table->maximum();
+                if (!actualMaximum) {
+                    return JSValue::encode(
+                        throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("Table import does not have a 'maximum' but the module requires that it does"))));
+                }
+                if (*actualMaximum > *expectedMaximum) {
+                    return JSValue::encode(
+                        throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("Imported Table's 'maximum' is larger than the module's expected 'maximum'"))));
+                }
+            }
+
             // ii. Append v to tables.
             // iii. Append v.[[Table]] to imports.
-            RELEASE_ASSERT_NOT_REACHED();
+            instance->setTable(vm, table);
             break;
         }
         case Wasm::External::Memory: {
@@ -185,7 +207,7 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
 
     {
         if (!!moduleInformation.memory && moduleInformation.memory.isImport()) {
-            // We should either have an import or we should have thrown an exception.
+            // We should either have a Memory import or we should have thrown an exception.
             RELEASE_ASSERT(hasMemoryImport);
         }
 
@@ -200,6 +222,25 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
         }
     }
 
+    {
+        if (!!moduleInformation.tableInformation && moduleInformation.tableInformation.isImport()) {
+            // We should either have a Table import or we should have thrown an exception.
+            RELEASE_ASSERT(hasTableImport);
+        }
+
+        if (!!moduleInformation.tableInformation && !hasTableImport) {
+            RELEASE_ASSERT(!moduleInformation.tableInformation.isImport());
+            // We create a Table when it's a Table definition.
+            JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(),
+                moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum());
+            // We should always be able to allocate a JSWebAssemblyTable we've defined.
+            // If it's defined to be too large, we should have thrown a validation error.
+            ASSERT(!throwScope.exception());
+            ASSERT(table); 
+            instance->setTable(vm, table);
+        }
+    }
+
     moduleRecord->link(exec, instance);
     RETURN_IF_EXCEPTION(throwScope, { });
     if (verbose)
index cf95311..f4b4103 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "FunctionPrototype.h"
 #include "JSCInlines.h"
+#include "JSWebAssemblyHelpers.h"
 #include "JSWebAssemblyMemory.h"
 #include "WasmMemory.h"
 #include "WasmPageCount.h"
@@ -54,17 +55,6 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyMemory(ExecState* exec
     if (exec->argumentCount() != 1)
         return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Memory expects exactly one argument"))));
 
-    auto getUint32 = [&] (JSValue value) -> uint32_t {
-        double doubleValue = value.toInteger(exec);
-        RETURN_IF_EXCEPTION(throwScope, { });
-        if (doubleValue < 0 || doubleValue > UINT_MAX) {
-            throwException(exec, throwScope,
-                createRangeError(exec, ASCIILiteral("WebAssembly.Memory expects the 'initial' and 'maximum' properties to be integers in the range: [0, 2^32 - 1]")));
-            return 0;
-        }
-        return static_cast<uint32_t>(doubleValue);
-    };
-
     JSObject* memoryDescriptor;
     {
         JSValue argument = exec->argument(0);
@@ -78,7 +68,7 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyMemory(ExecState* exec
         Identifier initial = Identifier::fromString(&vm, "initial");
         JSValue minSizeValue = memoryDescriptor->get(exec, initial);
         RETURN_IF_EXCEPTION(throwScope, { });
-        uint32_t size = getUint32(minSizeValue);
+        uint32_t size = toNonWrappingUint32(exec, minSizeValue);
         RETURN_IF_EXCEPTION(throwScope, { });
         if (!Wasm::PageCount::isValid(size))
             return JSValue::encode(throwException(exec, throwScope, createRangeError(exec, ASCIILiteral("WebAssembly.Memory 'initial' page count is too large"))));
@@ -93,10 +83,10 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyMemory(ExecState* exec
         if (hasProperty) {
             JSValue maxSizeValue = memoryDescriptor->get(exec, maximum);
             RETURN_IF_EXCEPTION(throwScope, { });
-            uint32_t size = getUint32(maxSizeValue);
+            uint32_t size = toNonWrappingUint32(exec, maxSizeValue);
+            RETURN_IF_EXCEPTION(throwScope, { });
             if (!Wasm::PageCount::isValid(size))
                 return JSValue::encode(throwException(exec, throwScope, createRangeError(exec, ASCIILiteral("WebAssembly.Memory 'maximum' page count is too large"))));
-            RETURN_IF_EXCEPTION(throwScope, { });
             maximumPageCount = Wasm::PageCount(size);
 
             if (initialPageCount > maximumPageCount) {
index 5983c03..8c8e3ad 100644 (file)
@@ -77,7 +77,7 @@ void WebAssemblyModuleRecord::finishCreation(ExecState* exec, VM& vm, const Wasm
             break;
         }
         case Wasm::External::Table: {
-            // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
+            // FIXME https://bugs.webkit.org/show_bug.cgi?id=165782
             break;
         }
         case Wasm::External::Memory: {
@@ -136,16 +136,17 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
             //     a. Let func be an Exported Function Exotic Object created from c.
             //     b. Append func to funcs.
             //     c. Return func.
-            JSWebAssemblyCallee* wasmCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(exp.functionIndex);
+            JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(exp.functionIndex);
+            JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(exp.functionIndex);
             Wasm::Signature* signature = module->signatureForFunctionIndexSpace(exp.functionIndex);
-            WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), exp.field.string(), instance, wasmCallee, signature);
+            WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), exp.field.string(), instance, jsEntrypointCallee, wasmEntrypointCallee, signature);
             exportedValue = function;
             if (hasStart && startFunctionIndexSpace == exp.functionIndex)
                 m_startFunction.set(vm, this, function);
             break;
         }
         case Wasm::External::Table: {
-            // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
+            // FIXME https://bugs.webkit.org/show_bug.cgi?id=165782
             break;
         }
         case Wasm::External::Memory: {
@@ -174,8 +175,9 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
         // FIXME can start call imports / tables? This assumes not. https://github.com/WebAssembly/design/issues/896
         if (!m_startFunction.get()) {
             // The start function wasn't added above. It must be a purely internal function.
-            JSWebAssemblyCallee* wasmCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
-            WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), "start", instance, wasmCallee, signature);
+            JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
+            JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
+            WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), "start", instance, jsEntrypointCallee, wasmEntrypointCallee, signature);
             m_startFunction.set(vm, this, function);
         }
     }
index c9580ba..a9ac152 100644 (file)
@@ -30,6 +30,8 @@
 
 #include "FunctionPrototype.h"
 #include "JSCInlines.h"
+#include "JSWebAssemblyHelpers.h"
+#include "JSWebAssemblyTable.h"
 #include "WebAssemblyTablePrototype.h"
 
 #include "WebAssemblyTableConstructor.lut.h"
@@ -43,12 +45,53 @@ const ClassInfo WebAssemblyTableConstructor::s_info = { "Function", &Base::s_inf
  @end
  */
 
-static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyTable(ExecState* state)
+static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyTable(ExecState* exec)
 {
-    VM& vm = state->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
-    return JSValue::encode(throwException(state, scope, createError(state, ASCIILiteral("WebAssembly doesn't yet implement the Table constructor property"))));
+    VM& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    JSObject* memoryDescriptor;
+    {
+        JSValue argument = exec->argument(0);
+        if (!argument.isObject())
+            return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Table expects its first argument to be an object"))));
+        memoryDescriptor = jsCast<JSObject*>(argument);
+    }
+
+    {
+        Identifier elementIdent = Identifier::fromString(&vm, "element");
+        JSValue elementValue = memoryDescriptor->get(exec, elementIdent);
+        RETURN_IF_EXCEPTION(throwScope, { });
+        String elementString = elementValue.toWTFString(exec);
+        RETURN_IF_EXCEPTION(throwScope, { });
+        if (elementString != "anyfunc")
+            return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Table expects its 'element' field to be the string 'anyfunc'"))));
+    }
+
+    Identifier initialIdent = Identifier::fromString(&vm, "initial");
+    JSValue initialSizeValue = memoryDescriptor->get(exec, initialIdent);
+    RETURN_IF_EXCEPTION(throwScope, { });
+    uint32_t initial = toNonWrappingUint32(exec, initialSizeValue);
+    RETURN_IF_EXCEPTION(throwScope, { });
+
+    std::optional<uint32_t> maximum;
+    Identifier maximumIdent = Identifier::fromString(&vm, "maximum");
+    bool hasProperty = memoryDescriptor->hasProperty(exec, maximumIdent);
+    RETURN_IF_EXCEPTION(throwScope, { });
+    if (hasProperty) {
+        JSValue maxSizeValue = memoryDescriptor->get(exec, maximumIdent);
+        RETURN_IF_EXCEPTION(throwScope, { });
+        maximum = toNonWrappingUint32(exec, maxSizeValue);
+        RETURN_IF_EXCEPTION(throwScope, { });
+
+        if (initial > *maximum) {
+            return JSValue::encode(throwException(exec, throwScope,
+                createRangeError(exec, ASCIILiteral("'maximum' property must be greater than or equal to the 'initial' property"))));
+        }
+    }
+
+    throwScope.release();
+    return JSValue::encode(JSWebAssemblyTable::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyTableStructure(), initial, maximum));
 }
 
 static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyTable(ExecState* state)
index 7aa394b..3b1a064 100644 (file)
@@ -30,6 +30,8 @@
 
 #include "FunctionPrototype.h"
 #include "JSCInlines.h"
+#include "JSWebAssemblyHelpers.h"
+#include "JSWebAssemblyTable.h"
 
 #include "WebAssemblyTablePrototype.lut.h"
 
@@ -42,10 +44,112 @@ const ClassInfo WebAssemblyTablePrototype::s_info = { "WebAssembly.Table.prototy
  @end
  */
 
-WebAssemblyTablePrototype* WebAssemblyTablePrototype::create(VM& vm, JSGlobalObject*, Structure* structure)
+static ALWAYS_INLINE JSWebAssemblyTable* getTable(ExecState* exec, VM& vm, JSValue v)
+{
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+    JSWebAssemblyTable* result = jsDynamicCast<JSWebAssemblyTable*>(v);
+    if (!result) {
+        throwException(exec, throwScope, 
+            createTypeError(exec, ASCIILiteral("expected |this| value to be an instance of WebAssembly.Table")));
+        return nullptr;
+    }
+    return result;
+}
+
+EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncLength(ExecState*);
+EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncGrow(ExecState*);
+EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncGet(ExecState*);
+EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncSet(ExecState*);
+
+EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncLength(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    JSWebAssemblyTable* table = getTable(exec, vm, exec->thisValue());
+    RETURN_IF_EXCEPTION(throwScope, { });
+    return JSValue::encode(jsNumber(table->size()));
+}
+
+EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncGrow(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    JSWebAssemblyTable* table = getTable(exec, vm, exec->thisValue());
+    RETURN_IF_EXCEPTION(throwScope, { });
+
+    uint32_t index = toNonWrappingUint32(exec, exec->argument(0));
+    RETURN_IF_EXCEPTION(throwScope, { });
+    if (!table->grow(index)) {
+        throwException(exec, throwScope,
+            createTypeError(exec, ASCIILiteral("WebAssembly.Table.prototype.grow could not grow the table")));
+        return { };
+    }
+
+    return JSValue::encode(jsUndefined());
+}
+
+EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncGet(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    JSWebAssemblyTable* table = getTable(exec, vm, exec->thisValue());
+    RETURN_IF_EXCEPTION(throwScope, { });
+
+    uint32_t index = toNonWrappingUint32(exec, exec->argument(0));
+    RETURN_IF_EXCEPTION(throwScope, { });
+    if (index >= table->size()) {
+        throwException(exec, throwScope,
+            createRangeError(exec, ASCIILiteral("WebAssembly.Table.prototype.get expects an integer less than the size of the table")));
+        return { };
+    }
+
+    if (WebAssemblyFunction* result = table->getFunction(index))
+        return JSValue::encode(result);
+    return JSValue::encode(jsNull());
+}
+
+EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncSet(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    JSWebAssemblyTable* table = getTable(exec, vm, exec->thisValue());
+    RETURN_IF_EXCEPTION(throwScope, { });
+
+    JSValue value = exec->argument(1);
+    WebAssemblyFunction* function = jsDynamicCast<WebAssemblyFunction*>(value);
+    if (!value.isNull() && !function) {
+        throwException(exec, throwScope,
+            createTypeError(exec, ASCIILiteral("WebAssembly.Table.prototype.set expects the second argument to be null or an instance of WebAssembly.Function")));
+        return { };
+    }
+
+    uint32_t index = toNonWrappingUint32(exec, exec->argument(0));
+    RETURN_IF_EXCEPTION(throwScope, { });
+
+    if (index >= table->size()) {
+        throwException(exec, throwScope,
+            createRangeError(exec, ASCIILiteral("WebAssembly.Table.prototype.set expects an integer less than the size of the table")));
+        return { };
+    }
+
+    if (value.isNull())
+        table->clearFunction(index);
+    else {
+        ASSERT(!!function);
+        table->setFunction(vm, index, function);
+    }
+    
+    return JSValue::encode(jsUndefined());
+}
+
+WebAssemblyTablePrototype* WebAssemblyTablePrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
 {
     auto* object = new (NotNull, allocateCell<WebAssemblyTablePrototype>(vm.heap)) WebAssemblyTablePrototype(vm, structure);
-    object->finishCreation(vm);
+    object->finishCreation(vm, globalObject);
     return object;
 }
 
@@ -54,9 +158,14 @@ Structure* WebAssemblyTablePrototype::createStructure(VM& vm, JSGlobalObject* gl
     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
 }
 
-void WebAssemblyTablePrototype::finishCreation(VM& vm)
+void WebAssemblyTablePrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
     Base::finishCreation(vm);
+
+    JSC_NATIVE_GETTER("length", webAssemblyTableProtoFuncLength, DontEnum | Accessor);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("grow", webAssemblyTableProtoFuncGrow, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("get", webAssemblyTableProtoFuncGet, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("set", webAssemblyTableProtoFuncSet, DontEnum, 2);
 }
 
 WebAssemblyTablePrototype::WebAssemblyTablePrototype(VM& vm, Structure* structure)
index c5b1ef8..5e04505 100644 (file)
@@ -43,7 +43,7 @@ public:
     DECLARE_INFO;
 
 protected:
-    void finishCreation(VM&);
+    void finishCreation(VM&, JSGlobalObject*);
 
 private:
     WebAssemblyTablePrototype(VM&, Structure*);