[WASM-References] Add support for multiple tables
authorjustin_michaud@apple.com <justin_michaud@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 18 Jun 2019 22:01:02 +0000 (22:01 +0000)
committerjustin_michaud@apple.com <justin_michaud@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 18 Jun 2019 22:01:02 +0000 (22:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=198760

Reviewed by Saam Barati.

JSTests:

* wasm/Builder.js:
* wasm/js-api/call-indirect.js:
(const.oneTable):
(const.multiTable):
(multiTable):
(multiTable.Polyphic2Import):
(multiTable.VirtualImport):
(const.wasmModuleWhichImportJS): Deleted.
(const.makeTable): Deleted.
(): Deleted.
(Polyphic2Import): Deleted.
(VirtualImport): Deleted.
* wasm/js-api/table.js:
(new.WebAssembly.Module):
(assert.throws):
(assertBadTableImport):
(assert.truthy):
(assert.throws.new.WebAssembly.Module.builder.WebAssembly): Deleted.
* wasm/references/anyref_table.js:
* wasm/references/anyref_table_import.js:
(makeImport):
(string_appeared_here.fullGC.assert.eq.1.exports.get_tbl.makeImport):
(string_appeared_here.fullGC.assert.eq.1.exports.get_tbl):
* wasm/references/multitable.js: Added.
(assert.throws.1.exports.set_tbl0):
(assert.throws):
(assert.eq):
* wasm/references/validation.js:
(assert.throws.new.WebAssembly.Module.bin):
(assert.throws):
* wasm/spec-tests/imports.wast.js:
* wasm/wasm.json:

* wasm/Builder.js:
* wasm/js-api/call-indirect.js:
(const.oneTable):
(const.multiTable):
(multiTable):
(multiTable.Polyphic2Import):
(multiTable.VirtualImport):
(const.wasmModuleWhichImportJS): Deleted.
(const.makeTable): Deleted.
(): Deleted.
(Polyphic2Import): Deleted.
(VirtualImport): Deleted.
* wasm/js-api/table.js:
(new.WebAssembly.Module):
(assert.throws):
(assertBadTableImport):
(assert.truthy):
(assert.throws.new.WebAssembly.Module.builder.WebAssembly): Deleted.
* wasm/references/anyref_table.js:
* wasm/references/anyref_table_import.js:
(makeImport):
(string_appeared_here.fullGC.assert.eq.1.exports.get_tbl.makeImport):
(string_appeared_here.fullGC.assert.eq.1.exports.get_tbl):
* wasm/references/func_ref.js:
(GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly.fun): Deleted.
(GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly.assert.throws): Deleted.
(GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly): Deleted.
* wasm/references/multitable.js: Added.
(assert.throws.1.exports.set_tbl0):
(assert.throws):
(assert.eq):
(string_appeared_here.tableInsanity):
(I32Const.0.GetLocal.0.TableSet.1.End.End.WebAssembly.):
(I32Const.0.GetLocal.0.TableSet.1.End.End.WebAssembly):
* wasm/references/validation.js:
(assert.throws.new.WebAssembly.Module.bin):
(assert.throws):
* wasm/spec-tests/imports.wast.js:
* wasm/wasm.json:

Source/JavaScriptCore:

Support multiple wasm tables. We turn tableInformation into a tables array, and update all of the
existing users to give a table index. The array of Tables in Wasm::Instance is hung off the tail
to make it easier to use from jit code.

* wasm/WasmAirIRGenerator.cpp:
(JSC::Wasm::AirIRGenerator::AirIRGenerator):
(JSC::Wasm::AirIRGenerator::addTableGet):
(JSC::Wasm::AirIRGenerator::addTableSet):
(JSC::Wasm::AirIRGenerator::addCallIndirect):
* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::B3IRGenerator):
(JSC::Wasm::B3IRGenerator::addTableGet):
(JSC::Wasm::B3IRGenerator::addTableSet):
(JSC::Wasm::B3IRGenerator::addCallIndirect):
* wasm/WasmExceptionType.h:
* wasm/WasmFormat.h:
(JSC::Wasm::Element::Element):
* wasm/WasmFunctionParser.h:
(JSC::Wasm::FunctionParser<Context>::parseExpression):
(JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression):
* wasm/WasmInstance.cpp:
(JSC::Wasm::Instance::Instance):
(JSC::Wasm::Instance::create):
(JSC::Wasm::Instance::extraMemoryAllocated const):
(JSC::Wasm::Instance::table):
(JSC::Wasm::Instance::setTable):
* wasm/WasmInstance.h:
(JSC::Wasm::Instance::updateCachedMemory):
(JSC::Wasm::Instance::offsetOfGlobals):
(JSC::Wasm::Instance::offsetOfTablePtr):
(JSC::Wasm::Instance::allocationSize):
(JSC::Wasm::Instance::table): Deleted.
(JSC::Wasm::Instance::setTable): Deleted.
(JSC::Wasm::Instance::offsetOfTable): Deleted.
* wasm/WasmModuleInformation.h:
(JSC::Wasm::ModuleInformation::tableCount const):
* wasm/WasmSectionParser.cpp:
(JSC::Wasm::SectionParser::parseImport):
(JSC::Wasm::SectionParser::parseTableHelper):
(JSC::Wasm::SectionParser::parseTable):
(JSC::Wasm::SectionParser::parseElement):
* wasm/WasmTable.h:
(JSC::Wasm::Table::owner const):
* wasm/WasmValidate.cpp:
(JSC::Wasm::Validate::addTableGet):
(JSC::Wasm::Validate::addTableSet):
(JSC::Wasm::Validate::addCallIndirect):
* wasm/js/JSWebAssemblyInstance.cpp:
(JSC::JSWebAssemblyInstance::JSWebAssemblyInstance):
(JSC::JSWebAssemblyInstance::visitChildren):
* wasm/js/JSWebAssemblyInstance.h:
* wasm/js/WebAssemblyModuleRecord.cpp:
(JSC::WebAssemblyModuleRecord::link):
(JSC::WebAssemblyModuleRecord::evaluate):
* wasm/wasm.json:

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

27 files changed:
JSTests/ChangeLog
JSTests/wasm/Builder.js
JSTests/wasm/js-api/call-indirect.js
JSTests/wasm/js-api/table.js
JSTests/wasm/references/anyref_table.js
JSTests/wasm/references/anyref_table_import.js
JSTests/wasm/references/func_ref.js
JSTests/wasm/references/multitable.js [new file with mode: 0644]
JSTests/wasm/references/validation.js
JSTests/wasm/spec-tests/imports.wast.js
JSTests/wasm/wasm.json
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmFunctionParser.h
Source/JavaScriptCore/wasm/WasmInstance.cpp
Source/JavaScriptCore/wasm/WasmInstance.h
Source/JavaScriptCore/wasm/WasmLimits.h
Source/JavaScriptCore/wasm/WasmModuleInformation.h
Source/JavaScriptCore/wasm/WasmSectionParser.cpp
Source/JavaScriptCore/wasm/WasmTable.h
Source/JavaScriptCore/wasm/WasmValidate.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp
Source/JavaScriptCore/wasm/wasm.json

index 789d22f..b4ee273 100644 (file)
@@ -1,3 +1,83 @@
+2019-06-18  Justin Michaud  <justin_michaud@apple.com>
+
+        [WASM-References] Add support for multiple tables
+        https://bugs.webkit.org/show_bug.cgi?id=198760
+
+        Reviewed by Saam Barati.
+
+        * wasm/Builder.js:
+        * wasm/js-api/call-indirect.js:
+        (const.oneTable):
+        (const.multiTable):
+        (multiTable):
+        (multiTable.Polyphic2Import):
+        (multiTable.VirtualImport):
+        (const.wasmModuleWhichImportJS): Deleted.
+        (const.makeTable): Deleted.
+        (): Deleted.
+        (Polyphic2Import): Deleted.
+        (VirtualImport): Deleted.
+        * wasm/js-api/table.js:
+        (new.WebAssembly.Module):
+        (assert.throws):
+        (assertBadTableImport):
+        (assert.truthy):
+        (assert.throws.new.WebAssembly.Module.builder.WebAssembly): Deleted.
+        * wasm/references/anyref_table.js:
+        * wasm/references/anyref_table_import.js:
+        (makeImport):
+        (string_appeared_here.fullGC.assert.eq.1.exports.get_tbl.makeImport):
+        (string_appeared_here.fullGC.assert.eq.1.exports.get_tbl):
+        * wasm/references/multitable.js: Added.
+        (assert.throws.1.exports.set_tbl0):
+        (assert.throws):
+        (assert.eq):
+        * wasm/references/validation.js:
+        (assert.throws.new.WebAssembly.Module.bin):
+        (assert.throws):
+        * wasm/spec-tests/imports.wast.js:
+        * wasm/wasm.json:
+
+        * wasm/Builder.js:
+        * wasm/js-api/call-indirect.js:
+        (const.oneTable):
+        (const.multiTable):
+        (multiTable):
+        (multiTable.Polyphic2Import):
+        (multiTable.VirtualImport):
+        (const.wasmModuleWhichImportJS): Deleted.
+        (const.makeTable): Deleted.
+        (): Deleted.
+        (Polyphic2Import): Deleted.
+        (VirtualImport): Deleted.
+        * wasm/js-api/table.js:
+        (new.WebAssembly.Module):
+        (assert.throws):
+        (assertBadTableImport):
+        (assert.truthy):
+        (assert.throws.new.WebAssembly.Module.builder.WebAssembly): Deleted.
+        * wasm/references/anyref_table.js:
+        * wasm/references/anyref_table_import.js:
+        (makeImport):
+        (string_appeared_here.fullGC.assert.eq.1.exports.get_tbl.makeImport):
+        (string_appeared_here.fullGC.assert.eq.1.exports.get_tbl):
+        * wasm/references/func_ref.js:
+        (GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly.fun): Deleted.
+        (GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly.assert.throws): Deleted.
+        (GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly): Deleted.
+        * wasm/references/multitable.js: Added.
+        (assert.throws.1.exports.set_tbl0):
+        (assert.throws):
+        (assert.eq):
+        (string_appeared_here.tableInsanity):
+        (I32Const.0.GetLocal.0.TableSet.1.End.End.WebAssembly.):
+        (I32Const.0.GetLocal.0.TableSet.1.End.End.WebAssembly):
+        * wasm/references/validation.js:
+        (assert.throws.new.WebAssembly.Module.bin):
+        (assert.throws):
+        * wasm/spec-tests/imports.wast.js:
+        * wasm/wasm.json:
+
 2019-06-18  Alexey Shvayka  <shvaikalesh@gmail.com>
 
         [ESNExt] String.prototype.matchAll
index 65f1f47..976377f 100644 (file)
@@ -306,6 +306,7 @@ const _checkImms = (op, imms, expectedImms, ret) => {
         case "target_count": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
         case "target_table": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
         case "reserved": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
+        case "table_index": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
         default: throw new Error(`Implementation problem: unhandled immediate "${expect.name}" on "${op}"`);
         }
     }
index 01cd42f..2fafcc1 100644 (file)
@@ -1,7 +1,7 @@
 import * as assert from '../assert.js';
 import Builder from '../Builder.js';
 
-const wasmModuleWhichImportJS = () => {
+const oneTable = () => {
     const builder = (new Builder())
         .Type().End()
         .Import()
@@ -31,6 +31,39 @@ const wasmModuleWhichImportJS = () => {
     return module;
 };
 
+const multiTable = () => {
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Function("imp", "func", { params: ["i32"] })
+            .Table("imp", "table0", { initial: 0, maximum: 0, element: "anyfunc"})
+            .Table("imp", "table", { initial: 1, maximum: 1, element: "anyfunc"})
+        .End()
+        .Function().End()
+        .Export()
+            .Function("changeCounter")
+            .Function("callFunc")
+        .End()
+        .Code()
+            .Function("changeCounter", { params: ["i32", "i32"] })
+                .I32Const(42)
+                .GetLocal(0)
+                .I32Add()
+                .GetLocal(1)
+                .CallIndirect(0, 1) // 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();
+    const module = new WebAssembly.Module(bin);
+    return module;
+};
+
+for (const wasmModuleWhichImportJS of [oneTable, multiTable]) {
+
 const makeTable = () => {
     return new WebAssembly.Table({initial: 1, maximum: 1, element: "anyfunc"});
 };
@@ -40,7 +73,7 @@ const makeTable = () => {
     const counterSetter = v => counter = v;
     const table = makeTable();
     const module = wasmModuleWhichImportJS();
-    const instance = new WebAssembly.Instance(module, { imp: { func: counterSetter, table} });
+    const instance = new WebAssembly.Instance(module, { imp: { func: counterSetter, table, table0: new WebAssembly.Table({initial: 0, maximum: 0, element: "anyfunc"}) } });
     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.
@@ -57,11 +90,11 @@ const makeTable = () => {
     const module = wasmModuleWhichImportJS();
 
     const tableA = makeTable();
-    const instanceA = new WebAssembly.Instance(module, { imp: { func: counterASetter, table: tableA} });
+    const instanceA = new WebAssembly.Instance(module, { imp: { func: counterASetter, table: tableA, table0: new WebAssembly.Table({initial: 0, maximum: 0, element: "anyfunc"}) } });
     tableA.set(0, instanceA.exports.callFunc);
 
     const tableB = makeTable();
-    const instanceB = new WebAssembly.Instance(module, { imp: { func: counterBSetter, table: tableB} });
+    const instanceB = new WebAssembly.Instance(module, { imp: { func: counterBSetter, table: tableB, table0: new WebAssembly.Table({initial: 0, maximum: 0, element: "anyfunc"}) } });
     tableB.set(0, instanceB.exports.callFunc);
     for (let i = 0; i < 2048; ++i) {
         instanceA.exports.changeCounter(i, 0);
@@ -93,7 +126,7 @@ const makeTable = () => {
     let instances = [];
     for (let i = 0; i < num; ++i) {
         let table = makeTable();
-        instances[i] = new WebAssembly.Instance(module, { imp: { func: counterSetters[i], table} });
+        instances[i] = new WebAssembly.Instance(module, { imp: { func: counterSetters[i], table, table0: new WebAssembly.Table({initial: 0, maximum: 0, element: "anyfunc"}) } });
         table.set(0, instances[i].exports.callFunc);
     }
     for (let i = 0; i < 2048; ++i) {
@@ -104,3 +137,5 @@ const makeTable = () => {
         }
     }
 })();
+
+}
index bbca89e..1b84443 100644 (file)
@@ -13,7 +13,7 @@ import * as assert from '../assert.js';
         .End()
         .Code()
         .End();
-    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 34: Cannot have more than one Table for now");
+    new WebAssembly.Module(builder.WebAssembly().get())
 }
 
 {
@@ -38,7 +38,7 @@ import * as assert from '../assert.js';
         .End()
         .Code()
         .End();
-    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 17: Table count of 2 is invalid, at most 1 is allowed for now (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
+    new WebAssembly.Module(builder.WebAssembly().get())
 }
 
 {
@@ -73,7 +73,28 @@ import * as assert from '../assert.js';
                 .CallIndirect(0, 1)
             .End()
         .End();
-    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 6: call_indirect's 'reserved' varuint1 must be 0x0, in function at index 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 6: call_indirect's table index 1 invalid, limit is 1, in function at index 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Function().End()
+        .Table()
+            .Table({initial:20, element:"anyfunc"})
+            .Table({initial:20, element:"anyfunc"})
+        .End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", {params: ["i32"]})
+                .GetLocal(0)
+                .GetLocal(0)
+                .CallIndirect(0, 1)
+            .End()
+        .End();
+    new WebAssembly.Module(builder.WebAssembly().get())
 }
 
 {
@@ -186,7 +207,7 @@ function assertBadTableImport(tableDescription, message) {
         .Function().End()
         .Code()
         .End();
-    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 39: Cannot have more than one Table for now");
+    new WebAssembly.Module(builder.WebAssembly().get())
 }
 
 
@@ -320,3 +341,26 @@ assert.throws(() => WebAssembly.Table.prototype.grow(undefined), TypeError, `exp
     assert.eq(instance.exports.table.length, 20);
     assert.truthy(instance.exports.table instanceof WebAssembly.Table);
 }
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Function().End()
+        .Table()
+            .Table({initial: 0, maximum: 1, element: "anyfunc"})
+            .Table({initial: 20, maximum: 30, element: "anyfunc"})
+        .End()
+        .Export()
+            .Table("table0", 0)
+            .Table("table", 1)
+            .Table("table2", 1)
+        .End()
+        .Code().End();
+
+    const module = new WebAssembly.Module(builder.WebAssembly().get());
+    const instance = new WebAssembly.Instance(module);
+    assert.eq(instance.exports.table, instance.exports.table2);
+    assert.eq(instance.exports.table.length, 20);
+    assert.eq(instance.exports.table0.length, 0);
+    assert.truthy(instance.exports.table instanceof WebAssembly.Table);
+}
index b2bcaec..bc95f67 100644 (file)
@@ -18,12 +18,12 @@ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
         .Function("set_tbl", { params: ["anyref"], ret: "void" })
           .I32Const(0)
           .GetLocal(0)
-          .TableSet()
+          .TableSet(0)
         .End()
 
         .Function("get_tbl", { params: [], ret: "anyref" })
           .I32Const(0)
-          .TableGet()
+          .TableGet(0)
         .End()
 
         .Function("tbl_is_null", { params: [], ret: "i32" })
index 88cab7c..1742bb2 100644 (file)
@@ -22,12 +22,12 @@ import Builder from '../Builder.js';
             .Function("set_tbl", { params: ["anyref"], ret: "void" })
               .I32Const(0)
               .GetLocal(0)
-              .TableSet()
+              .TableSet(0)
             .End()
 
             .Function("get_tbl", { params: [], ret: "anyref" })
               .I32Const(0)
-              .TableGet()
+              .TableGet(0)
             .End()
 
             .Function("tbl_is_null", { params: [], ret: "i32" })
@@ -88,12 +88,12 @@ import Builder from '../Builder.js';
             .Function("set_tbl", { params: ["anyref"], ret: "void" })
               .I32Const(0)
               .GetLocal(0)
-              .TableSet()
+              .TableSet(0)
             .End()
 
             .Function("get_tbl", { params: [], ret: "anyref" })
               .I32Const(0)
-              .TableGet()
+              .TableGet(0)
             .End()
 
             .Function("tbl_is_null", { params: [], ret: "i32" })
@@ -127,12 +127,12 @@ import Builder from '../Builder.js';
             .Function("set_tbl", { params: ["anyref"], ret: "void" })
               .I32Const(0)
               .GetLocal(0)
-              .TableSet()
+              .TableSet(0)
             .End()
 
             .Function("get_tbl", { params: [], ret: "anyref" })
               .I32Const(0)
-              .TableGet()
+              .TableGet(0)
             .End()
 
             .Function("tbl_is_null", { params: [], ret: "i32" })
index 35e06e4..36a79b6 100644 (file)
@@ -257,7 +257,7 @@ assert.throws(() => new WebAssembly.Module((new Builder())
     .Function("h", { params: ["i32"], ret: "void" })
       .GetLocal(0)
       .I32Const(0)
-      .TableSet()
+      .TableSet(0)
     .End()
   .End().WebAssembly().get()), Error, "WebAssembly.Module doesn't validate: table.set value to type I32 expected Anyfunc, in function at index 0 (evaluating 'new WebAssembly.Module')");
 
@@ -267,6 +267,7 @@ assert.throws(() => new WebAssembly.Module((new Builder())
       .Type().End()
       .Function().End()
       .Table()
+            .Table({initial: 0, element: "anyref"})
             .Table({initial: 1, element: "anyfunc"})
       .End()
       .Global()
@@ -291,11 +292,11 @@ assert.throws(() => new WebAssembly.Module((new Builder())
         .Function("call_glob", { params: ["i32"], ret: "i32" })
             .I32Const(0)
             .GetGlobal(0)
-            .TableSet()
+            .TableSet(1)
 
             .GetLocal(0)
             .I32Const(0)
-            .CallIndirect(2,0)
+            .CallIndirect(2,1)
         .End()
 
         .Function("ret_20", { params: ["i32"], ret: "i32" })
@@ -328,6 +329,7 @@ assert.throws(() => new WebAssembly.Module((new Builder())
       .Type().End()
       .Function().End()
       .Table()
+            .Table({initial: 0, element: "anyref"})
             .Table({initial: 1, element: "anyfunc"})
       .End()
       .Export()
@@ -338,12 +340,12 @@ assert.throws(() => new WebAssembly.Module((new Builder())
         .Function("set", { params: ["anyfunc"], ret: "void" })
           .I32Const(0)
           .GetLocal(0)
-          .TableSet()
+          .TableSet(1)
         .End()
 
         .Function("get", { params: [], ret: "anyfunc" })
             .I32Const(0)
-            .TableGet()
+            .TableGet(1)
         .End()
       .End().WebAssembly().get()));
 
@@ -405,7 +407,7 @@ for (let importedFun of [function(i) { return i; }, makeAnyfuncIdent()]) {
           .GetLocal(0)
           .I32Const(0)
           .RefFunc(0)
-          .TableSet()
+          .TableSet(0)
           .I32Const(0)
           .CallIndirect(0, 0)
         .End()
@@ -414,7 +416,7 @@ for (let importedFun of [function(i) { return i; }, makeAnyfuncIdent()]) {
           .RefFunc(1)
           .I32Const(0)
           .RefFunc(0)
-          .TableSet()
+          .TableSet(0)
           .I32Const(0)
           .CallIndirect(0, 0)
         .End()
diff --git a/JSTests/wasm/references/multitable.js b/JSTests/wasm/references/multitable.js
new file mode 100644 (file)
index 0000000..371c2f0
--- /dev/null
@@ -0,0 +1,460 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+{
+    const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 0, maximum: 0, element: "anyref"})
+                .Table({initial: 20, maximum: 30, element: "anyref"})
+          .End()
+          .Export()
+              .Function("set_tbl")
+              .Function("get_tbl")
+              .Function("get_tbl0")
+              .Function("set_tbl0")
+              .Table("tbl", 1)
+          .End()
+          .Code()
+            .Function("set_tbl", { params: ["anyref"], ret: "void" })
+              .I32Const(0)
+              .GetLocal(0)
+              .TableSet(1)
+            .End()
+
+            .Function("get_tbl", { params: [], ret: "anyref" })
+              .I32Const(0)
+              .TableGet(1)
+            .End()
+
+            .Function("get_tbl0", { params: [], ret: "anyref" })
+              .I32Const(0)
+              .TableGet(0)
+            .End()
+
+            .Function("set_tbl0", { params: ["anyref"], ret: "void" })
+              .I32Const(0)
+              .GetLocal(0)
+              .TableSet(0)
+            .End()
+          .End().WebAssembly().get()));
+
+    fullGC()
+
+    assert.eq($1.exports.get_tbl(), null)
+
+    $1.exports.set_tbl("hi")
+    fullGC()
+    assert.eq($1.exports.get_tbl(), "hi")
+    assert.eq($1.exports.tbl.get(0), "hi")
+    assert.eq($1.exports.tbl.get(1), null)
+
+    assert.throws(() => $1.exports.get_tbl0(), Error, "Out of bounds table access (evaluating 'func(...args)')");
+    assert.throws(() => $1.exports.set_tbl0(null), Error, "Out of bounds table access (evaluating 'func(...args)')");
+}
+
+{
+    const tbl = new WebAssembly.Table({initial:0, element:"anyref"});
+    const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Import()
+                .Table("imp", "tbl", {initial: 0, element: "anyref"})
+          .End()
+          .Function().End()
+          .Table()
+                .Table({initial: 20, maximum: 30, element: "anyref"})
+          .End()
+          .Export()
+              .Function("set_tbl")
+              .Function("get_tbl")
+              .Function("get_tbl0")
+              .Function("set_tbl0")
+              .Table("tbl", 1)
+          .End()
+          .Code()
+            .Function("set_tbl", { params: ["anyref"], ret: "void" })
+              .I32Const(0)
+              .GetLocal(0)
+              .TableSet(1)
+            .End()
+
+            .Function("get_tbl", { params: [], ret: "anyref" })
+              .I32Const(0)
+              .TableGet(1)
+            .End()
+
+            .Function("get_tbl0", { params: [], ret: "anyref" })
+              .I32Const(0)
+              .TableGet(0)
+            .End()
+
+            .Function("set_tbl0", { params: ["anyref"], ret: "void" })
+              .I32Const(0)
+              .GetLocal(0)
+              .TableSet(0)
+            .End()
+          .End().WebAssembly().get()), { imp: { tbl }});
+
+    fullGC()
+
+    assert.eq($1.exports.get_tbl(), null)
+
+    $1.exports.set_tbl("hi")
+    fullGC()
+    assert.eq($1.exports.get_tbl(), "hi")
+    assert.eq($1.exports.tbl.get(0), "hi")
+    assert.eq($1.exports.tbl.get(1), null)
+
+    assert.throws(() => $1.exports.get_tbl0(), Error, "Out of bounds table access (evaluating 'func(...args)')");
+    assert.throws(() => $1.exports.set_tbl0(null), Error, "Out of bounds table access (evaluating 'func(...args)')");
+}
+
+{
+    const tbl = new WebAssembly.Table({initial:1, element:"anyref"});
+    const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Import()
+                .Table("imp", "tbl", {initial: 1, element: "anyref"})
+          .End()
+          .Function().End()
+          .Table()
+                .Table({initial: 20, maximum: 30, element: "anyref"})
+          .End()
+          .Export()
+              .Function("set_tbl")
+              .Function("get_tbl")
+              .Function("get_tbl0")
+              .Function("set_tbl0")
+              .Table("tbl", 1)
+          .End()
+          .Code()
+            .Function("set_tbl", { params: ["anyref"], ret: "void" })
+              .I32Const(0)
+              .GetLocal(0)
+              .TableSet(1)
+            .End()
+
+            .Function("get_tbl", { params: [], ret: "anyref" })
+              .I32Const(0)
+              .TableGet(1)
+            .End()
+
+            .Function("get_tbl0", { params: [], ret: "anyref" })
+              .I32Const(0)
+              .TableGet(0)
+            .End()
+
+            .Function("set_tbl0", { params: ["anyref"], ret: "void" })
+              .I32Const(0)
+              .GetLocal(0)
+              .TableSet(0)
+            .End()
+          .End().WebAssembly().get()), { imp: { tbl }});
+
+    fullGC()
+
+    assert.eq($1.exports.get_tbl(), null)
+
+    $1.exports.set_tbl("hi")
+    fullGC()
+    $1.exports.set_tbl0(null)
+    assert.eq($1.exports.get_tbl(), "hi")
+    assert.eq($1.exports.get_tbl0(), null)
+    assert.eq($1.exports.tbl.get(0), "hi")
+    assert.eq($1.exports.tbl.get(1), null)
+    assert.eq(tbl.get(0), null)
+}
+
+{
+    const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 3, maximum: 30, element: "anyfunc"})
+                .Table({initial: 2, maximum: 30, element: "anyfunc"})
+          .End()
+          .Export()
+              .Function("call_tbl0")
+              .Function("call_tbl1")
+              .Function("ret42")
+              .Function("ret1337")
+              .Function("ret256")
+          .End()
+          .Element()
+                .Element({tableIndex: 1, offset: 0, functionIndices: [2]})
+          .End()
+          .Code()
+            .Function("call_tbl0", { params: ["i32"], ret: "i32" })
+              .GetLocal(0)
+              .CallIndirect(1,0)
+            .End()
+
+            .Function("call_tbl1", { params: ["i32"], ret: "i32" })
+              .GetLocal(0)
+              .CallIndirect(1,1)
+            .End()
+
+            .Function("ret42", { params: [], ret: "i32" })
+              .I32Const(42)
+            .End()
+
+            .Function("ret1337", { params: [], ret: "i32" })
+              .I32Const(1337)
+            .End()
+
+            .Function("ret256", { params: [], ret: "i32" })
+              .I32Const(256)
+            .End()
+          .End().WebAssembly().get()));
+
+    fullGC()
+
+    assert.eq($1.exports.call_tbl1(0), 42)
+    assert.throws(() => $1.exports.call_tbl0(0), Error, "call_indirect to a null table entry (evaluating 'func(...args)')")
+    assert.throws(() => $1.exports.call_tbl1(1), Error, "call_indirect to a null table entry (evaluating 'func(...args)')")
+}
+
+{
+    const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 3, maximum: 30, element: "anyfunc"})
+                .Table({initial: 2, maximum: 30, element: "anyfunc"})
+          .End()
+          .Export()
+              .Function("call_tbl0")
+              .Function("call_tbl1")
+              .Function("ret42")
+              .Function("ret1337")
+              .Function("ret256")
+          .End()
+          .Element()
+                .Element({tableIndex: 1, offset: 0, functionIndices: [2]})
+                .Element({tableIndex: 0, offset: 0, functionIndices: [3]})
+                .Element({tableIndex: 1, offset: 1, functionIndices: [4]})
+          .End()
+          .Code()
+            .Function("call_tbl0", { params: ["i32"], ret: "i32" })
+              .GetLocal(0)
+              .CallIndirect(1,0)
+            .End()
+
+            .Function("call_tbl1", { params: ["i32"], ret: "i32" })
+              .GetLocal(0)
+              .CallIndirect(1,1)
+            .End()
+
+            .Function("ret42", { params: [], ret: "i32" })
+              .I32Const(42)
+            .End()
+
+            .Function("ret1337", { params: [], ret: "i32" })
+              .I32Const(1337)
+            .End()
+
+            .Function("ret256", { params: [], ret: "i32" })
+              .I32Const(256)
+            .End()
+          .End().WebAssembly().get()));
+
+    fullGC()
+
+    assert.eq($1.exports.call_tbl1(0), 42)
+    assert.eq($1.exports.call_tbl0(0), 1337)
+    assert.eq($1.exports.call_tbl1(1), 256)
+    assert.throws(() => $1.exports.call_tbl0(1), Error, "call_indirect to a null table entry (evaluating 'func(...args)')")
+    assert.throws(() => $1.exports.call_tbl0(2), Error, "call_indirect to a null table entry (evaluating 'func(...args)')")
+    assert.throws(() => $1.exports.call_tbl1(2), Error, "Out of bounds call_indirect (evaluating 'func(...args)')")
+}
+ assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 3, maximum: 3, element: "anyfunc"})
+                .Table({initial: 2, maximum: 2, element: "anyfunc"})
+          .End()
+          .Element()
+                .Element({tableIndex: 1, offset: 0, functionIndices: [0]})
+                .Element({tableIndex: 0, offset: 0, functionIndices: [0]})
+                .Element({tableIndex: 1, offset: 2, functionIndices: [0]})
+          .End()
+          .Code()
+            .Function("ret42", { params: [], ret: "i32" })
+              .I32Const(42)
+            .End()
+          .End().WebAssembly().get())), Error, "Element is trying to set an out of bounds table index (evaluating 'new WebAssembly.Instance')")
+
+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 3, maximum: 3, element: "anyref"})
+                .Table({initial: 2, maximum: 3, element: "anyfunc"})
+          .End()
+          .Element()
+                .Element({tableIndex: 1, offset: 0, functionIndices: [0]})
+                .Element({tableIndex: 0, offset: 0, functionIndices: [0]})
+                .Element({tableIndex: 1, offset: 2, functionIndices: [0]})
+          .End()
+          .Code()
+            .Function("ret42", { params: [], ret: "i32" })
+              .I32Const(42)
+            .End()
+          .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't parse at byte 40: Table 0 must have type 'anyfunc' to have an element section (evaluating 'new WebAssembly.Module')")
+
+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 3, maximum: 3, element: "anyref"})
+                .Table({initial: 2, maximum: 3, element: "anyfunc"})
+          .End()
+          .Code()
+            .Function("fun", { params: [], ret: "anyref" })
+              .I32Const(0)
+              .TableGet(2)
+            .End()
+          .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't validate: table index 2 is invalid, limit is 2, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 3, maximum: 3, element: "anyref"})
+                .Table({initial: 2, maximum: 3, element: "anyfunc"})
+          .End()
+          .Code()
+            .Function("fun", { params: [], ret: "void" })
+              .I32Const(0)
+              .RefNull()
+              .TableSet(2)
+            .End()
+          .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't validate: table index 2 is invalid, limit is 2, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 3, maximum: 3, element: "anyref"})
+                .Table({initial: 2, maximum: 3, element: "anyfunc"})
+          .End()
+          .Code()
+            .Function("fun", { params: [], ret: "void" })
+              .CallIndirect(0, 2)
+            .End()
+          .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't parse at byte 4: call_indirect's table index 2 invalid, limit is 2, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 3, maximum: 3, element: "anyref"})
+                .Table({initial: 2, maximum: 3, element: "anyfunc"})
+          .End()
+          .Code()
+            .Function("fun", { params: [], ret: "void" })
+              .CallIndirect(0,0)
+            .End()
+          .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't parse at byte 4: call_indirect is only valid when a table has type anyfunc, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 3, maximum: 3, element: "anyref"})
+                .Table({initial: 2, maximum: 3, element: "anyfunc"})
+          .End()
+          .Code()
+            .Function("fun", { params: [], ret: "void" })
+              .RefNull()
+              .TableGet(0)
+            .End()
+          .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't validate: table.get index to type Anyfunc expected I32, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+
+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 3, maximum: 3, element: "anyref"})
+                .Table({initial: 2, maximum: 3, element: "anyfunc"})
+          .End()
+          .Code()
+            .Function("fun", { params: [], ret: "void" })
+              .RefNull()
+              .RefNull()
+              .TableSet(0)
+            .End()
+          .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't validate: table.set index to type Anyfunc expected I32, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+          .Type().End()
+          .Function().End()
+          .Table()
+                .Table({initial: 3, maximum: 3, element: "anyref"})
+                .Table({initial: 2, maximum: 3, element: "anyfunc"})
+          .End()
+          .Code()
+            .Function("fun", { params: ["anyref"], ret: "void" })
+              .I32Const(0)
+              .GetLocal(0)
+              .TableSet(1)
+            .End()
+          .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't validate: table.set value to type Anyref expected Anyfunc, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+function tableInsanity(num, b) {
+    b = b.Import()
+    for (let i=0; i<100000-1; ++i)
+        b = b.Function("imp", "ref", { params: [], ret: "void" })
+    b = b.End().Function().End().Table()
+    for (let i=0; i<num; ++i)
+        b = b.Table({initial: 0, maximum: 3, element: "anyref"})
+    return b
+}
+
+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module(tableInsanity(1000000, (new Builder())
+          .Type().End())
+                .Table({initial: 3, maximum: 3, element: "anyref"})
+          .End()
+          .Code()
+            .Function("fun", { params: ["anyref"], ret: "void" })
+              .I32Const(0)
+              .GetLocal(0)
+              .TableSet(1)
+            .End()
+          .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't parse at byte 5000027: Table count of 1000000 is too big, maximum 1000000 (evaluating 'new WebAssembly.Module')")
+{
+    const $1 = new WebAssembly.Instance(new WebAssembly.Module(tableInsanity(1000000-2, (new Builder())
+          .Type().End())
+                .Table({initial: 3, maximum: 3, element: "anyfunc"})
+                .Table({initial: 3, maximum: 3, element: "anyref"})
+          .End()
+          .Export()
+                .Function("set_tbl")
+                .Function("get_tbl")
+                .Function("call")
+          .End()
+          .Element()
+                .Element({tableIndex: 1000000-2, offset: 0, functionIndices: [0]})
+          .End()
+          .Code()
+            .Function("set_tbl", { params: ["anyref"], ret: "void" })
+              .I32Const(0)
+              .GetLocal(0)
+              .TableSet(1000000-1)
+            .End()
+            .Function("get_tbl", { params: [], ret: "anyref" })
+              .I32Const(0)
+              .TableGet(1000000-1)
+            .End()
+            .Function("call", { params: [], ret: "void" })
+              .I32Const(0)
+              .CallIndirect(0, 1000000-2)
+            .End()
+          .End().WebAssembly().get()), { imp: { ref: function () {} } })
+    $1.exports.set_tbl("hi")
+    assert.eq($1.exports.get_tbl(), "hi")
+    $1.exports.call()
+}
index 8f81636..7f7d184 100644 (file)
@@ -24,6 +24,9 @@ import Builder from '../Builder.js';
 {
     const builder = (new Builder())
       .Type().End()
+      .Import()
+            .Table("imp", "tbl", {initial: 2, element: "anyfunc"})
+      .End()
       .Function().End()
       .Export()
           .Function("j")
@@ -32,14 +35,14 @@ import Builder from '../Builder.js';
         .Function("j", { params: [], ret: "void" })
             .I32Const(0)
             .I32Const(0)
-            .TableSet()
+            .TableSet(0)
         .End()
       .End();
 
     const bin = builder.WebAssembly();
     bin.trim();
 
-    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: table.set value to type I32 expected Anyref, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')");
+    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: table.set value to type I32 expected Anyfunc, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')");
 }
 
 {
@@ -56,7 +59,7 @@ import Builder from '../Builder.js';
         .Function("j", { params: ["anyref"], ret: "void" })
             .I32Const(0)
             .GetLocal(0)
-            .TableSet()
+            .TableSet(0)
         .End()
       .End();
 
@@ -79,7 +82,7 @@ import Builder from '../Builder.js';
       .Code()
         .Function("j", { params: [], ret: "anyref" })
             .I32Const(0)
-            .TableGet()
+            .TableGet(0)
         .End()
       .End();
 
index 48d1409..6b3faaf 100644 (file)
@@ -188,15 +188,6 @@ assert_trap(() => call($14, "call", [3]));
 // imports.wast:290
 assert_trap(() => call($14, "call", [100]));
 
-// imports.wast:293
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x8d\x80\x80\x80\x00\x02\x00\x00\x01\x70\x00\x0a\x00\x00\x01\x70\x00\x0a");
-
-// imports.wast:297
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x87\x80\x80\x80\x00\x01\x00\x00\x01\x70\x00\x0a\x04\x84\x80\x80\x80\x00\x01\x70\x00\x0a");
-
-// imports.wast:301
-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x04\x87\x80\x80\x80\x00\x02\x70\x00\x0a\x70\x00\x0a");
-
 // imports.wast:306
 let $15 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x97\x80\x80\x80\x00\x01\x04\x74\x65\x73\x74\x0c\x74\x61\x62\x6c\x65\x2d\x31\x30\x2d\x69\x6e\x66\x01\x70\x00\x0a");
 
index 64ca0b5..45308fb 100644 (file)
         "tee_local":           { "category": "special",    "value":  34, "return": ["any"],      "parameter": ["any"],                  "immediate": [{"name": "local_index",    "type": "varuint32"}],                                            "description": "write a local variable or parameter and return the same value" },
         "get_global":          { "category": "special",    "value":  35, "return": ["any"],      "parameter": [],                       "immediate": [{"name": "global_index",   "type": "varuint32"}],                                            "description": "read a global variable" },
         "set_global":          { "category": "special",    "value":  36, "return": [],           "parameter": ["any"],                  "immediate": [{"name": "global_index",   "type": "varuint32"}],                                            "description": "write a global variable" },
-        "table.get":           { "category": "special",    "value":  37, "return": ["anyref"],   "parameter": ["i32"],                  "immediate": [],                                                                                           "description": "get a table value" },
-        "table.set":           { "category": "special",    "value":  38, "return": [],           "parameter": ["i32", "anyref"],        "immediate": [],                                                                                           "description": "set a table value" },
+        "table.get":           { "category": "special",    "value":  37, "return": ["anyref"],   "parameter": ["i32"],                  "immediate": [{"name": "table_index",    "type": "varuint32"}],                                            "description": "get a table value" },
+        "table.set":           { "category": "special",    "value":  38, "return": [],           "parameter": ["i32", "anyref"],        "immediate": [{"name": "table_index",    "type": "varuint32"}],                                            "description": "set a table value" },
         "call":                { "category": "call",       "value":  16, "return": ["call"],     "parameter": ["call"],                 "immediate": [{"name": "function_index", "type": "varuint32"}],                                            "description": "call a function by its index" },
-        "call_indirect":       { "category": "call",       "value":  17, "return": ["call"],     "parameter": ["call"],                 "immediate": [{"name": "type_index",     "type": "varuint32"}, {"name": "reserved", "type": "varuint1"}],  "description": "call a function indirect with an expected signature" },
+        "call_indirect":       { "category": "call",       "value":  17, "return": ["call"],     "parameter": ["call"],                 "immediate": [{"name": "type_index",     "type": "varuint32"}, {"name": "table_index","type": "varuint32"}],"description": "call a function indirect with an expected signature" },
         "i32.load8_s":         { "category": "memory",     "value":  44, "return": ["i32"],      "parameter": ["addr"],                 "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset",   "type": "varuint32"}], "description": "load from memory" },
         "i32.load8_u":         { "category": "memory",     "value":  45, "return": ["i32"],      "parameter": ["addr"],                 "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset",   "type": "varuint32"}], "description": "load from memory" },
         "i32.load16_s":        { "category": "memory",     "value":  46, "return": ["i32"],      "parameter": ["addr"],                 "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset",   "type": "varuint32"}], "description": "load from memory" },
index e2016f3..8f81f5f 100644 (file)
@@ -1,3 +1,66 @@
+2019-06-18  Justin Michaud  <justin_michaud@apple.com>
+
+        [WASM-References] Add support for multiple tables
+        https://bugs.webkit.org/show_bug.cgi?id=198760
+
+        Reviewed by Saam Barati.
+
+        Support multiple wasm tables. We turn tableInformation into a tables array, and update all of the
+        existing users to give a table index. The array of Tables in Wasm::Instance is hung off the tail
+        to make it easier to use from jit code. 
+
+        * wasm/WasmAirIRGenerator.cpp:
+        (JSC::Wasm::AirIRGenerator::AirIRGenerator):
+        (JSC::Wasm::AirIRGenerator::addTableGet):
+        (JSC::Wasm::AirIRGenerator::addTableSet):
+        (JSC::Wasm::AirIRGenerator::addCallIndirect):
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::B3IRGenerator):
+        (JSC::Wasm::B3IRGenerator::addTableGet):
+        (JSC::Wasm::B3IRGenerator::addTableSet):
+        (JSC::Wasm::B3IRGenerator::addCallIndirect):
+        * wasm/WasmExceptionType.h:
+        * wasm/WasmFormat.h:
+        (JSC::Wasm::Element::Element):
+        * wasm/WasmFunctionParser.h:
+        (JSC::Wasm::FunctionParser<Context>::parseExpression):
+        (JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression):
+        * wasm/WasmInstance.cpp:
+        (JSC::Wasm::Instance::Instance):
+        (JSC::Wasm::Instance::create):
+        (JSC::Wasm::Instance::extraMemoryAllocated const):
+        (JSC::Wasm::Instance::table):
+        (JSC::Wasm::Instance::setTable):
+        * wasm/WasmInstance.h:
+        (JSC::Wasm::Instance::updateCachedMemory):
+        (JSC::Wasm::Instance::offsetOfGlobals):
+        (JSC::Wasm::Instance::offsetOfTablePtr):
+        (JSC::Wasm::Instance::allocationSize):
+        (JSC::Wasm::Instance::table): Deleted.
+        (JSC::Wasm::Instance::setTable): Deleted.
+        (JSC::Wasm::Instance::offsetOfTable): Deleted.
+        * wasm/WasmModuleInformation.h:
+        (JSC::Wasm::ModuleInformation::tableCount const):
+        * wasm/WasmSectionParser.cpp:
+        (JSC::Wasm::SectionParser::parseImport):
+        (JSC::Wasm::SectionParser::parseTableHelper):
+        (JSC::Wasm::SectionParser::parseTable):
+        (JSC::Wasm::SectionParser::parseElement):
+        * wasm/WasmTable.h:
+        (JSC::Wasm::Table::owner const):
+        * wasm/WasmValidate.cpp:
+        (JSC::Wasm::Validate::addTableGet):
+        (JSC::Wasm::Validate::addTableSet):
+        (JSC::Wasm::Validate::addCallIndirect):
+        * wasm/js/JSWebAssemblyInstance.cpp:
+        (JSC::JSWebAssemblyInstance::JSWebAssemblyInstance):
+        (JSC::JSWebAssemblyInstance::visitChildren):
+        * wasm/js/JSWebAssemblyInstance.h:
+        * wasm/js/WebAssemblyModuleRecord.cpp:
+        (JSC::WebAssemblyModuleRecord::link):
+        (JSC::WebAssemblyModuleRecord::evaluate):
+        * wasm/wasm.json:
+
 2019-06-18  Alexey Shvayka  <shvaikalesh@gmail.com>
 
         [ESNExt] String.prototype.matchAll
index ab304a5..eff7e32 100644 (file)
@@ -237,8 +237,8 @@ public:
     PartialResult WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result);
 
     // Tables
-    PartialResult WARN_UNUSED_RETURN addTableGet(ExpressionType& index, ExpressionType& result);
-    PartialResult WARN_UNUSED_RETURN addTableSet(ExpressionType& index, ExpressionType& value);
+    PartialResult WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType& index, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType& index, ExpressionType& value);
 
     // Locals
     PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
@@ -277,7 +277,7 @@ public:
 
     // Calls
     PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, ExpressionType& result);
-    PartialResult WARN_UNUSED_RETURN addCallIndirect(const Signature&, Vector<ExpressionType>& args, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN addCallIndirect(unsigned tableIndex, const Signature&, Vector<ExpressionType>& args, ExpressionType& result);
     PartialResult WARN_UNUSED_RETURN addUnreachable();
 
     PartialResult addShift(Type, B3::Air::Opcode, ExpressionType value, ExpressionType shift, ExpressionType& result);
@@ -626,6 +626,7 @@ private:
     }
 
     uint32_t m_maxNumJSCallArguments { 0 };
+    unsigned m_numImportFunctions;
 
     B3::PatchpointSpecial* m_patchpointSpecial { nullptr };
 };
@@ -681,6 +682,7 @@ AirIRGenerator::AirIRGenerator(const ModuleInformation& info, B3::Procedure& pro
     , m_proc(procedure)
     , m_code(m_proc.code())
     , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls)
+    , m_numImportFunctions(info.importFunctionCount())
 {
     m_currentBlock = m_code.addBlock();
     m_rootBlock = m_currentBlock;
@@ -968,14 +970,14 @@ auto AirIRGenerator::addRefFunc(uint32_t index, ExpressionType& result) -> Parti
     return { };
 }
 
-auto AirIRGenerator::addTableGet(ExpressionType& index, ExpressionType& result) -> PartialResult
+auto AirIRGenerator::addTableGet(unsigned tableIndex, ExpressionType& index, ExpressionType& result) -> PartialResult
 {
     // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>.
     ASSERT(index.tmp());
     ASSERT(index.type() == Type::I32);
     result = tmpForType(Type::Anyref);
 
-    emitCCall(&getWasmTableElement, result, instanceValue(), index);
+    emitCCall(&getWasmTableElement, result, instanceValue(), addConstant(Type::I32, tableIndex), index);
     emitCheck([&] {
         return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::Zero), result, result);
     }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
@@ -985,7 +987,7 @@ auto AirIRGenerator::addTableGet(ExpressionType& index, ExpressionType& result)
     return { };
 }
 
-auto AirIRGenerator::addTableSet(ExpressionType& index, ExpressionType& value) -> PartialResult
+auto AirIRGenerator::addTableSet(unsigned tableIndex, ExpressionType& index, ExpressionType& value) -> PartialResult
 {
     // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>.
     ASSERT(index.tmp());
@@ -993,7 +995,7 @@ auto AirIRGenerator::addTableSet(ExpressionType& index, ExpressionType& value) -
     ASSERT(value.tmp());
 
     auto shouldThrow = g32();
-    emitCCall(&setWasmTableElement, shouldThrow, instanceValue(), index, value);
+    emitCCall(&setWasmTableElement, shouldThrow, instanceValue(), addConstant(Type::I32, tableIndex), index, value);
 
     emitCheck([&] {
         return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::Zero), shouldThrow, shouldThrow);
@@ -1893,11 +1895,12 @@ auto AirIRGenerator::addCall(uint32_t functionIndex, const Signature& signature,
     return { };
 }
 
-auto AirIRGenerator::addCallIndirect(const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult
+auto AirIRGenerator::addCallIndirect(unsigned tableIndex, const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult
 {
     ExpressionType calleeIndex = args.takeLast();
     ASSERT(signature.argumentCount() == args.size());
-    ASSERT(m_info.tableInformation.type() == TableElementType::Funcref);
+    ASSERT(m_info.tableCount() > tableIndex);
+    ASSERT(m_info.tables[tableIndex].type() == TableElementType::Funcref);
 
     m_makesCalls = true;
     // Note: call indirect can call either WebAssemblyFunction or WebAssemblyWrapperFunction. Because
@@ -1912,12 +1915,16 @@ auto AirIRGenerator::addCallIndirect(const Signature& signature, Vector<Expressi
     ExpressionType instancesBuffer = g64();
     ExpressionType callableFunctionBufferLength = g64();
     {
-        RELEASE_ASSERT(Arg::isValidAddrForm(Instance::offsetOfTable(), B3::Width64));
         RELEASE_ASSERT(Arg::isValidAddrForm(FuncRefTable::offsetOfFunctions(), B3::Width64));
         RELEASE_ASSERT(Arg::isValidAddrForm(FuncRefTable::offsetOfInstances(), B3::Width64));
         RELEASE_ASSERT(Arg::isValidAddrForm(FuncRefTable::offsetOfLength(), B3::Width64));
 
-        append(Move, Arg::addr(instanceValue(), Instance::offsetOfTable()), callableFunctionBufferLength);
+        if (UNLIKELY(!Arg::isValidAddrForm(Instance::offsetOfTablePtr(m_numImportFunctions, tableIndex), B3::Width64))) {
+            append(Move, Arg::bigImm(Instance::offsetOfTablePtr(m_numImportFunctions, tableIndex)), callableFunctionBufferLength);
+            append(Add64, instanceValue(), callableFunctionBufferLength);
+            append(Move, Arg::addr(callableFunctionBufferLength), callableFunctionBufferLength);
+        } else
+            append(Move, Arg::addr(instanceValue(), Instance::offsetOfTablePtr(m_numImportFunctions, tableIndex)), callableFunctionBufferLength);
         append(Move, Arg::addr(callableFunctionBufferLength, FuncRefTable::offsetOfFunctions()), callableFunctionBuffer);
         append(Move, Arg::addr(callableFunctionBufferLength, FuncRefTable::offsetOfInstances()), instancesBuffer);
         append(Move32, Arg::addr(callableFunctionBufferLength, Table::offsetOfLength()), callableFunctionBufferLength);
index 7760974..1eb2465 100644 (file)
@@ -190,8 +190,8 @@ public:
     PartialResult WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result);
 
     // Tables
-    PartialResult WARN_UNUSED_RETURN addTableGet(ExpressionType& index, ExpressionType& result);
-    PartialResult WARN_UNUSED_RETURN addTableSet(ExpressionType& index, ExpressionType& value);
+    PartialResult WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType& index, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType& index, ExpressionType& value);
 
     // Locals
     PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
@@ -230,7 +230,7 @@ public:
 
     // Calls
     PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, ExpressionType& result);
-    PartialResult WARN_UNUSED_RETURN addCallIndirect(const Signature&, Vector<ExpressionType>& args, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN addCallIndirect(unsigned tableIndex, const Signature&, Vector<ExpressionType>& args, ExpressionType& result);
     PartialResult WARN_UNUSED_RETURN addUnreachable();
 
     void dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack);
@@ -292,6 +292,7 @@ private:
     }
 
     uint32_t m_maxNumJSCallArguments { 0 };
+    unsigned m_numImportFunctions;
 };
 
 // Memory accesses in WebAssembly have unsigned 32-bit offsets, whereas they have signed 32-bit offsets in B3.
@@ -343,6 +344,7 @@ B3IRGenerator::B3IRGenerator(const ModuleInformation& info, Procedure& procedure
     , m_proc(procedure)
     , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls)
     , m_constantInsertionValues(m_proc)
+    , m_numImportFunctions(info.importFunctionCount())
 {
     m_currentBlock = m_proc.addBlock();
 
@@ -566,12 +568,12 @@ auto B3IRGenerator::addRefIsNull(ExpressionType& value, ExpressionType& result)
     return { };
 }
 
-auto B3IRGenerator::addTableGet(ExpressionType& index, ExpressionType& result) -> PartialResult
+auto B3IRGenerator::addTableGet(unsigned tableIndex, ExpressionType& index, ExpressionType& result) -> PartialResult
 {
     // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>.
     result = m_currentBlock->appendNew<CCallValue>(m_proc, toB3Type(Anyref), origin(),
         m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(&getWasmTableElement, B3CCallPtrTag)),
-        instanceValue(), index);
+        instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex), index);
 
     {
         CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
@@ -585,12 +587,12 @@ auto B3IRGenerator::addTableGet(ExpressionType& index, ExpressionType& result) -
     return { };
 }
 
-auto B3IRGenerator::addTableSet(ExpressionType& index, ExpressionType& value) -> PartialResult
+auto B3IRGenerator::addTableSet(unsigned tableIndex, ExpressionType& index, ExpressionType& value) -> PartialResult
 {
     // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>.
     auto shouldThrow = m_currentBlock->appendNew<CCallValue>(m_proc, B3::Int32, origin(),
         m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(&setWasmTableElement, B3CCallPtrTag)),
-        instanceValue(), index, value);
+        instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex), index, value);
 
     {
         CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
@@ -1298,7 +1300,7 @@ auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature& signature,
     return { };
 }
 
-auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult
+auto B3IRGenerator::addCallIndirect(unsigned tableIndex, const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult
 {
     ExpressionType calleeIndex = args.takeLast();
     ASSERT(signature.argumentCount() == args.size());
@@ -1315,7 +1317,7 @@ auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<Expressio
     ExpressionType mask;
     {
         ExpressionType table = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
-            instanceValue(), safeCast<int32_t>(Instance::offsetOfTable()));
+            instanceValue(), safeCast<int32_t>(Instance::offsetOfTablePtr(m_numImportFunctions, tableIndex)));
         callableFunctionBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
             table, safeCast<int32_t>(FuncRefTable::offsetOfFunctions()));
         instancesBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
index 7c03441..4db28a4 100644 (file)
@@ -213,10 +213,12 @@ struct Segment {
 };
 
 struct Element {
-    Element(I32InitExpr offset)
-        : offset(offset)
+    Element(uint32_t tableIndex, I32InitExpr offset)
+        : tableIndex(tableIndex)
+        , offset(offset)
     { }
 
+    uint32_t tableIndex;
     I32InitExpr offset;
     Vector<uint32_t> functionIndices;
 };
index 59eee26..7250542 100644 (file)
@@ -283,19 +283,23 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult
 
     case TableGet: {
         WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled");
+        unsigned tableIndex;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't parse table index");
         ExpressionType result, index;
         WASM_TRY_POP_EXPRESSION_STACK_INTO(index, "table.get");
-        WASM_TRY_ADD_TO_CONTEXT(addTableGet(index, result));
+        WASM_TRY_ADD_TO_CONTEXT(addTableGet(tableIndex, index, result));
         m_expressionStack.append(result);
         return { };
     }
 
     case TableSet: {
         WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled");
+        unsigned tableIndex;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't parse table index");
         ExpressionType val, index;
         WASM_TRY_POP_EXPRESSION_STACK_INTO(val, "table.set");
         WASM_TRY_POP_EXPRESSION_STACK_INTO(index, "table.set");
-        WASM_TRY_ADD_TO_CONTEXT(addTableSet(index, val));
+        WASM_TRY_ADD_TO_CONTEXT(addTableSet(tableIndex, index, val));
         return { };
     }
 
@@ -396,13 +400,13 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult
 
     case CallIndirect: {
         uint32_t signatureIndex;
-        uint8_t reserved;
-        WASM_PARSER_FAIL_IF(!m_info.tableInformation, "call_indirect is only valid when a table is defined or imported");
+        uint32_t tableIndex;
+        WASM_PARSER_FAIL_IF(!m_info.tableCount(), "call_indirect is only valid when a table is defined or imported");
         WASM_PARSER_FAIL_IF(!parseVarUInt32(signatureIndex), "can't get call_indirect's signature index");
-        WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't get call_indirect's reserved byte");
-        WASM_PARSER_FAIL_IF(reserved, "call_indirect's 'reserved' varuint1 must be 0x0");
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't get call_indirect's table index");
+        WASM_PARSER_FAIL_IF(tableIndex >= m_info.tableCount(), "call_indirect's table index ", tableIndex, " invalid, limit is ", m_info.tableCount());
         WASM_PARSER_FAIL_IF(m_info.usedSignatures.size() <= signatureIndex, "call_indirect's signature index ", signatureIndex, " exceeds known signatures ", m_info.usedSignatures.size());
-        WASM_PARSER_FAIL_IF(m_info.tableInformation.type() != TableElementType::Funcref, "call_indirect is only valid when a table has type anyfunc");
+        WASM_PARSER_FAIL_IF(m_info.tables[tableIndex].type() != TableElementType::Funcref, "call_indirect is only valid when a table has type anyfunc");
 
         const Signature& calleeSignature = m_info.usedSignatures[signatureIndex].get();
         size_t argumentCount = calleeSignature.argumentCount() + 1; // Add the callee's index.
@@ -416,7 +420,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult
         m_expressionStack.shrink(firstArgumentIndex);
 
         ExpressionType result = Context::emptyExpression();
-        WASM_TRY_ADD_TO_CONTEXT(addCallIndirect(calleeSignature, args, result));
+        WASM_TRY_ADD_TO_CONTEXT(addCallIndirect(tableIndex, calleeSignature, args, result));
 
         if (result != Context::emptyExpression())
             m_expressionStack.append(result);
@@ -631,9 +635,9 @@ auto FunctionParser<Context>::parseUnreachableExpression() -> PartialResult
 
     case CallIndirect: {
         uint32_t unused;
-        uint8_t unused2;
+        uint32_t unused2;
         WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get call_indirect's signature index in unreachable context");
-        WASM_PARSER_FAIL_IF(!parseVarUInt1(unused2), "can't get call_indirect's reserved byte in unreachable context");
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(unused2), "can't get call_indirect's reserved byte in unreachable context");
         return { };
     }
 
@@ -685,7 +689,11 @@ auto FunctionParser<Context>::parseUnreachableExpression() -> PartialResult
     }
 
     case TableGet:
-    case TableSet:
+    case TableSet: {
+        unsigned tableIndex;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't parse table index");
+        FALLTHROUGH;
+    }
     case RefIsNull:
     case RefNull: {
         WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled");
index 52bf0f2..c41549b 100644 (file)
@@ -61,18 +61,19 @@ Instance::Instance(Context* context, Ref<Module>&& module, EntryFrame** pointerT
         if (isSubtype(m_module.get().moduleInformation().globals[i].type, Anyref))
             m_globalsToMark.set(i);
     }
+    memset(bitwise_cast<char*>(this) + offsetOfTablePtr(m_numImportFunctions, 0), 0, m_module->moduleInformation().tableCount() * sizeof(Table*));
 }
 
 Ref<Instance> Instance::create(Context* context, Ref<Module>&& module, EntryFrame** pointerToTopEntryFrame, void** pointerToActualStackLimit, StoreTopCallFrameCallback&& storeTopCallFrame)
 {
-    return adoptRef(*new (NotNull, fastMalloc(allocationSize(module->moduleInformation().importFunctionCount()))) Instance(context, WTFMove(module), pointerToTopEntryFrame, pointerToActualStackLimit, WTFMove(storeTopCallFrame)));
+    return adoptRef(*new (NotNull, fastMalloc(allocationSize(module->moduleInformation().importFunctionCount(), module->moduleInformation().tableCount()))) Instance(context, WTFMove(module), pointerToTopEntryFrame, pointerToActualStackLimit, WTFMove(storeTopCallFrame)));
 }
 
 Instance::~Instance() { }
 
 size_t Instance::extraMemoryAllocated() const
 {
-    return globalMemoryByteSize(m_module.get()) + allocationSize(m_numImportFunctions);
+    return globalMemoryByteSize(m_module.get()) + allocationSize(m_numImportFunctions, m_module->moduleInformation().tableCount());
 }
 
 void Instance::setGlobal(unsigned i, JSValue value)
@@ -99,42 +100,57 @@ void Instance::setFunctionWrapper(unsigned i, JSValue value)
     ASSERT(getFunctionWrapper(i) == value);
 }
 
-EncodedJSValue getWasmTableElement(Instance* instance, int32_t signedIndex)
+Table* Instance::table(unsigned i)
 {
+    RELEASE_ASSERT(i < m_module->moduleInformation().tableCount());
+    return *bitwise_cast<Table**>(bitwise_cast<char*>(this) + offsetOfTablePtr(m_numImportFunctions, i));
+}
+
+void Instance::setTable(unsigned i, Ref<Table>&& table)
+{
+    RELEASE_ASSERT(i < m_module->moduleInformation().tableCount());
+    ASSERT(!this->table(i));
+    *bitwise_cast<Table**>(bitwise_cast<char*>(this) + offsetOfTablePtr(m_numImportFunctions, i)) = &table.leakRef();
+}
+
+EncodedJSValue getWasmTableElement(Instance* instance, unsigned tableIndex, int32_t signedIndex)
+{
+    ASSERT(tableIndex < instance->module().moduleInformation().tableCount());
     if (signedIndex < 0)
         return 0;
 
     uint32_t index = signedIndex;
-    if (index >= instance->table()->length())
+    if (index >= instance->table(tableIndex)->length())
         return 0;
 
-    return JSValue::encode(instance->table()->get(index));
+    return JSValue::encode(instance->table(tableIndex)->get(index));
 }
 
-bool setWasmTableElement(Instance* instance, int32_t signedIndex, EncodedJSValue encValue)
+bool setWasmTableElement(Instance* instance, unsigned tableIndex, int32_t signedIndex, EncodedJSValue encValue)
 {
+    ASSERT(tableIndex < instance->module().moduleInformation().tableCount());
     if (signedIndex < 0)
         return false;
 
     uint32_t index = signedIndex;
-    if (index >= instance->table()->length())
+    if (index >= instance->table(tableIndex)->length())
         return false;
 
     JSValue value = JSValue::decode(encValue);
-    if (instance->table()->type() == Wasm::TableElementType::Anyref)
-        instance->table()->set(index, value);
-    else if (instance->table()->type() == Wasm::TableElementType::Funcref) {
+    if (instance->table(tableIndex)->type() == Wasm::TableElementType::Anyref)
+        instance->table(tableIndex)->set(index, value);
+    else if (instance->table(tableIndex)->type() == Wasm::TableElementType::Funcref) {
         WebAssemblyFunction* wasmFunction;
         WebAssemblyWrapperFunction* wasmWrapperFunction;
 
         if (isWebAssemblyHostFunction(*instance->owner<JSObject>()->vm(), value, wasmFunction, wasmWrapperFunction)) {
             ASSERT(!!wasmFunction || !!wasmWrapperFunction);
             if (wasmFunction)
-                instance->table()->asFuncrefTable()->setFunction(index, jsCast<JSObject*>(value), wasmFunction->importableFunction(), &wasmFunction->instance()->instance());
+                instance->table(tableIndex)->asFuncrefTable()->setFunction(index, jsCast<JSObject*>(value), wasmFunction->importableFunction(), &wasmFunction->instance()->instance());
             else
-                instance->table()->asFuncrefTable()->setFunction(index, jsCast<JSObject*>(value), wasmWrapperFunction->importableFunction(), &wasmWrapperFunction->instance()->instance());
+                instance->table(tableIndex)->asFuncrefTable()->setFunction(index, jsCast<JSObject*>(value), wasmWrapperFunction->importableFunction(), &wasmWrapperFunction->instance()->instance());
         } else if (value.isNull())
-            instance->table()->clear(index);
+            instance->table(tableIndex)->clear(index);
         else
             ASSERT_NOT_REACHED();
     } else
index 7a23e56..012cd45 100644 (file)
@@ -41,8 +41,8 @@ namespace JSC { namespace Wasm {
 struct Context;
 class Instance;
 
-EncodedJSValue getWasmTableElement(Instance*, int32_t);
-bool setWasmTableElement(Instance*, int32_t, EncodedJSValue encValue);
+EncodedJSValue getWasmTableElement(Instance*, unsigned, int32_t);
+bool setWasmTableElement(Instance*, unsigned, int32_t, EncodedJSValue encValue);
 EncodedJSValue doWasmRefFunc(Instance*, uint32_t);
 
 class Instance : public ThreadSafeRefCounted<Instance>, public CanMakeWeakPtr<Instance> {
@@ -70,7 +70,8 @@ public:
     Module& module() { return m_module.get(); }
     CodeBlock* codeBlock() { return m_codeBlock.get(); }
     Memory* memory() { return m_memory.get(); }
-    Table* table() { return m_table.get(); }
+    Table* table(unsigned);
+    void setTable(unsigned, Ref<Table>&&);
 
     void* cachedMemory() const { return m_cachedMemory.getMayBeNull(cachedMemorySize()); }
     size_t cachedMemorySize() const { return m_cachedMemorySize; }
@@ -88,7 +89,6 @@ public:
             m_cachedMemorySize = memory()->size();
         }
     }
-    void setTable(Ref<Table>&& table) { m_table = WTFMove(table); }
 
     int32_t loadI32Global(unsigned i) const { return m_globals.get()[i].primitive; }
     int64_t loadI64Global(unsigned i) const { return m_globals.get()[i].primitive; }
@@ -103,7 +103,6 @@ public:
 
     static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(Instance, m_memory); }
     static ptrdiff_t offsetOfGlobals() { return OBJECT_OFFSETOF(Instance, m_globals); }
-    static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(Instance, m_table); }
     static ptrdiff_t offsetOfCachedMemory() { return OBJECT_OFFSETOF(Instance, m_cachedMemory); }
     static ptrdiff_t offsetOfCachedMemorySize() { return OBJECT_OFFSETOF(Instance, m_cachedMemorySize); }
     static ptrdiff_t offsetOfPointerToTopEntryFrame() { return OBJECT_OFFSETOF(Instance, m_pointerToTopEntryFrame); }
@@ -122,7 +121,7 @@ public:
     }
 
     // Tail accessors.
-    static size_t offsetOfTail() { return WTF::roundUpToMultipleOf<sizeof(uint64_t)>(sizeof(Instance)); }
+    static constexpr size_t offsetOfTail() { return WTF::roundUpToMultipleOf<sizeof(uint64_t)>(sizeof(Instance)); }
     struct ImportFunctionInfo {
         // Target instance and entrypoint are only set for wasm->wasm calls, and are otherwise nullptr. The embedder-specific logic occurs through import function.
         Instance* targetInstance { nullptr };
@@ -142,6 +141,9 @@ public:
     static size_t offsetOfImportFunction(size_t importFunctionNum) { return offsetOfTail() + importFunctionNum * sizeof(ImportFunctionInfo) + OBJECT_OFFSETOF(ImportFunctionInfo, importFunction); }
     template<typename T> T* importFunction(unsigned importFunctionNum) { return reinterpret_cast<T*>(&importFunctionInfo(importFunctionNum)->importFunction); }
 
+    static_assert(sizeof(ImportFunctionInfo) == WTF::roundUpToMultipleOf<sizeof(uint64_t)>(sizeof(ImportFunctionInfo)), "We rely on this for the alignment to be correct");
+    static constexpr size_t offsetOfTablePtr(unsigned numImportFunctions, unsigned i) { return offsetOfTail() + sizeof(ImportFunctionInfo) * numImportFunctions + sizeof(Table*) * i; }
+
     void storeTopCallFrame(void* callFrame)
     {
         m_storeTopCallFrame(callFrame);
@@ -150,9 +152,9 @@ public:
 private:
     Instance(Context*, Ref<Module>&&, EntryFrame**, void**, StoreTopCallFrameCallback&&);
     
-    static size_t allocationSize(Checked<size_t> numImportFunctions)
+    static size_t allocationSize(Checked<size_t> numImportFunctions, Checked<size_t> numTables)
     {
-        return (offsetOfTail() + sizeof(ImportFunctionInfo) * numImportFunctions).unsafeGet();
+        return (offsetOfTail() + sizeof(ImportFunctionInfo) * numImportFunctions + sizeof(Table*) * numTables).unsafeGet();
     }
     void* m_owner { nullptr }; // In a JS embedding, this is a JSWebAssemblyInstance*.
     Context* m_context { nullptr };
@@ -161,7 +163,6 @@ private:
     Ref<Module> m_module;
     RefPtr<CodeBlock> m_codeBlock;
     RefPtr<Memory> m_memory;
-    RefPtr<Table> m_table;
 
     union GlobalValue {
         WriteBarrier<Unknown> anyref;
index c6f6cdc..cf8b446 100644 (file)
@@ -51,6 +51,7 @@ constexpr size_t maxFunctionLocals = 50000;
 constexpr size_t maxFunctionParams = 1000;
 
 constexpr size_t maxTableEntries = 10000000;
+constexpr unsigned maxTables = 1000000;
 
 } } // namespace JSC::Wasm
 
index 88f66cb..268f88b 100644 (file)
@@ -65,7 +65,7 @@ struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> {
     // Currently, our wasm implementation allows only one memory and table.
     // If we need to remove this limitation, we would have MemoryInformation and TableInformation in the Vectors.
     uint32_t memoryCount() const { return memory ? 1 : 0; }
-    uint32_t tableCount() const { return tableInformation ? 1 : 0; }
+    uint32_t tableCount() const { return tables.size(); }
 
     const BitVector& referencedFunctions() const { return m_referencedFunctions; }
     void addReferencedFunction(unsigned index) const { m_referencedFunctions.set(index); }
@@ -83,7 +83,7 @@ struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> {
     Optional<uint32_t> startFunctionIndexSpace;
     Vector<Segment::Ptr> data;
     Vector<Element> elements;
-    TableInformation tableInformation;
+    Vector<TableInformation> tables;
     Vector<Global> globals;
     unsigned firstInternalGlobal { 0 };
     Vector<CustomSection> customSections;
index c80314d..ef51216 100644 (file)
@@ -119,6 +119,7 @@ auto SectionParser::parseImport() -> PartialResult
         }
         case ExternalKind::Table: {
             bool isImport = true;
+            kindIndex = m_info->tables.size();
             PartialResult result = parseTableHelper(isImport);
             if (UNLIKELY(!result))
                 return makeUnexpected(WTFMove(result.error()));
@@ -193,7 +194,7 @@ auto SectionParser::parseResizableLimits(uint32_t& initial, Optional<uint32_t>&
 
 auto SectionParser::parseTableHelper(bool isImport) -> PartialResult
 {
-    WASM_PARSER_FAIL_IF(m_info->tableCount() > 0, "Cannot have more than one Table for now");
+    WASM_PARSER_FAIL_IF(m_info->tableCount() >= maxTables, "Table count of ", m_info->tableCount(), " is too big, maximum ", maxTables);
 
     int8_t type;
     WASM_PARSER_FAIL_IF(!parseInt7(type), "can't parse Table type");
@@ -209,7 +210,7 @@ auto SectionParser::parseTableHelper(bool isImport) -> PartialResult
     ASSERT(!maximum || *maximum >= initial);
 
     TableElementType tableType = type == Wasm::Anyfunc ? TableElementType::Funcref : TableElementType::Anyref;
-    m_info->tableInformation = TableInformation(initial, maximum, isImport, tableType);
+    m_info->tables.append(TableInformation(initial, maximum, isImport, tableType));
 
     return { };
 }
@@ -218,15 +219,13 @@ auto SectionParser::parseTable() -> PartialResult
 {
     uint32_t count;
     WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Table's count");
-    WASM_PARSER_FAIL_IF(count > 1, "Table count of ", count, " is invalid, at most 1 is allowed for now");
 
-    if (!count)
-        return { };
-
-    bool isImport = false;
-    PartialResult result = parseTableHelper(isImport);
-    if (UNLIKELY(!result))
-        return makeUnexpected(WTFMove(result.error()));
+    for (unsigned i = 0; i < count; ++i) {
+        bool isImport = false;
+        PartialResult result = parseTableHelper(isImport);
+        if (UNLIKELY(!result))
+            return makeUnexpected(WTFMove(result.error()));
+    }
 
     return { };
 }
@@ -379,16 +378,16 @@ auto SectionParser::parseElement() -> PartialResult
 
         WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't get ", elementNum, "th Element table index");
         WASM_PARSER_FAIL_IF(tableIndex >= m_info->tableCount(), "Element section for Table ", tableIndex, " exceeds available Table ", m_info->tableCount());
-        WASM_PARSER_FAIL_IF(m_info->tableInformation.type() != TableElementType::Funcref, "Table ", tableIndex, " must have type 'anyfunc' to have an element section");
+        WASM_PARSER_FAIL_IF(m_info->tables[tableIndex].type() != TableElementType::Funcref, "Table ", tableIndex, " must have type 'anyfunc' to have an element section");
         Type initExprType;
         WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, initExprBits, initExprType));
         WASM_PARSER_FAIL_IF(initExprType != I32, "Element init_expr must produce an i32");
         WASM_PARSER_FAIL_IF(!parseVarUInt32(indexCount), "can't get ", elementNum, "th index count for Element section");
         WASM_PARSER_FAIL_IF(indexCount == std::numeric_limits<uint32_t>::max(), "Element section's ", elementNum, "th index count is too big ", indexCount);
 
-        ASSERT(!!m_info->tableInformation);
+        ASSERT(!!m_info->tables[tableIndex]);
 
-        Element element(makeI32InitExpr(initOpcode, initExprBits));
+        Element element(tableIndex, makeI32InitExpr(initOpcode, initExprBits));
         WASM_PARSER_FAIL_IF(!element.functionIndices.tryReserveCapacity(indexCount), "can't allocate memory for ", indexCount, " Element indices");
 
         for (unsigned index = 0; index < indexCount; ++index) {
index b1e2b5d..d6aac5b 100644 (file)
@@ -57,6 +57,7 @@ public:
     static uint32_t allocatedLength(uint32_t length);
     uint32_t mask() const { return m_mask; }
 
+    template<typename T> T* owner() const { return reinterpret_cast<T*>(m_owner); }
     void setOwner(JSObject* owner)
     {
         ASSERT(!m_owner);
index 7f1ac61..bd21d80 100644 (file)
@@ -106,8 +106,8 @@ public:
     Result WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result);
 
     // Tables
-    Result WARN_UNUSED_RETURN addTableGet(ExpressionType& index, ExpressionType& result);
-    Result WARN_UNUSED_RETURN addTableSet(ExpressionType& index, ExpressionType& value);
+    Result WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType& index, ExpressionType& result);
+    Result WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType& index, ExpressionType& value);
 
     // Locals
     Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
@@ -148,7 +148,7 @@ public:
 
     // Calls
     Result WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature&, const Vector<ExpressionType>& args, ExpressionType& result);
-    Result WARN_UNUSED_RETURN addCallIndirect(const Signature&, const Vector<ExpressionType>& args, ExpressionType& result);
+    Result WARN_UNUSED_RETURN addCallIndirect(unsigned tableIndex, const Signature&, const Vector<ExpressionType>& args, ExpressionType& result);
 
     ALWAYS_INLINE void didKill(ExpressionType) { }
 
@@ -178,21 +178,22 @@ auto Validate::addArguments(const Signature& signature) -> Result
     return { };
 }
 
-auto Validate::addTableGet(ExpressionType& index, ExpressionType& result) -> Result
+auto Validate::addTableGet(unsigned tableIndex, ExpressionType& index, ExpressionType& result) -> Result
 {
-    result = m_module.tableInformation.wasmType();
+    WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount());
+    result = m_module.tables[tableIndex].wasmType();
     WASM_VALIDATOR_FAIL_IF(Type::I32 != index, "table.get index to type ", index, " expected ", Type::I32);
 
     return { };
 }
 
-auto Validate::addTableSet(ExpressionType& index, ExpressionType& value) -> Result
+auto Validate::addTableSet(unsigned tableIndex, ExpressionType& index, ExpressionType& value) -> Result
 {
-    auto type = m_module.tableInformation.wasmType();
+    WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount());
+    auto type = m_module.tables[tableIndex].wasmType();
     WASM_VALIDATOR_FAIL_IF(Type::I32 != index, "table.set index to type ", index, " expected ", Type::I32);
     WASM_VALIDATOR_FAIL_IF(!isSubtype(value, type), "table.set value to type ", value, " expected ", type);
-    WASM_VALIDATOR_FAIL_IF(m_module.tableInformation.type() != TableElementType::Anyref
-        && m_module.tableInformation.type() != TableElementType::Funcref, "table.set expects the table to have type anyref or anyfunc");
+    RELEASE_ASSERT(m_module.tables[tableIndex].type() == TableElementType::Anyref || m_module.tables[tableIndex].type() == TableElementType::Funcref);
 
     return { };
 }
@@ -379,9 +380,10 @@ auto Validate::addCall(unsigned, const Signature& signature, const Vector<Expres
     return { };
 }
 
-auto Validate::addCallIndirect(const Signature& signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result
+auto Validate::addCallIndirect(unsigned tableIndex, const Signature& signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result
 {
-    WASM_VALIDATOR_FAIL_IF(m_module.tableInformation.type() != TableElementType::Funcref, "Table must have type Anyfunc to call");
+    RELEASE_ASSERT(tableIndex < m_module.tableCount());
+    RELEASE_ASSERT(m_module.tables[tableIndex].type() == TableElementType::Funcref);
     const auto argumentCount = signature.argumentCount();
     WASM_VALIDATOR_FAIL_IF(argumentCount != args.size() - 1, "arity mismatch in call_indirect, got ", args.size() - 1, " arguments, expected ", argumentCount);
 
index 323427f..99afa1a 100644 (file)
@@ -53,6 +53,7 @@ JSWebAssemblyInstance::JSWebAssemblyInstance(VM& vm, Structure* structure, Ref<W
     : Base(vm, structure)
     , m_instance(WTFMove(instance))
     , m_vm(&vm)
+    , m_tables(m_instance->module().moduleInformation().tableCount())
 {
     for (unsigned i = 0; i < this->instance().numImportFunctions(); ++i)
         new (this->instance().importFunction<WriteBarrier<JSObject>>(i)) WriteBarrier<JSObject>();
@@ -85,7 +86,8 @@ void JSWebAssemblyInstance::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(thisObject->m_codeBlock);
     visitor.append(thisObject->m_moduleNamespaceObject);
     visitor.append(thisObject->m_memory);
-    visitor.append(thisObject->m_table);
+    for (unsigned i = 0; i < thisObject->instance().module().moduleInformation().tableCount(); ++i)
+        visitor.append(thisObject->m_tables[i]);
     visitor.append(thisObject->m_callee);
     visitor.reportExtraMemoryVisited(thisObject->m_instance->extraMemoryAllocated());
     for (unsigned i = 0; i < thisObject->instance().numImportFunctions(); ++i)
index 21784a9..e1631fc 100644 (file)
@@ -71,11 +71,13 @@ public:
     }
     Wasm::MemoryMode memoryMode() { return memory()->memory().mode(); }
 
-    JSWebAssemblyTable* table() { return m_table.get(); }
-    void setTable(VM& vm, JSWebAssemblyTable* value) {
-        ASSERT(!table());
-        m_table.set(vm, this, value);
-        instance().setTable(makeRef(*table()->table()));
+    JSWebAssemblyTable* table(unsigned i) { return m_tables[i].get(); }
+    void setTable(VM& vm, uint32_t index, JSWebAssemblyTable* value)
+    {
+        ASSERT(index < m_tables.size());
+        ASSERT(!table(index));
+        m_tables[index].set(vm, this, value);
+        instance().setTable(index, makeRef(*table(index)->table()));
     }
 
     JSWebAssemblyModule* module() const { return m_module.get(); }
@@ -98,7 +100,7 @@ private:
     WriteBarrier<JSWebAssemblyCodeBlock> m_codeBlock;
     WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
     WriteBarrier<JSWebAssemblyMemory> m_memory;
-    WriteBarrier<JSWebAssemblyTable> m_table;
+    Vector<WriteBarrier<JSWebAssemblyTable>> m_tables;
     WriteBarrier<WebAssemblyToJSCallee> m_callee;
 };
 
index 12b1cef..052d085 100644 (file)
@@ -113,8 +113,6 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj
         return makeString(before, " ", String::fromUTF8(import.module), ":", String::fromUTF8(import.field), " ", after);
     };
 
-    bool hasTableImport = false;
-
     for (const auto& import : moduleInformation.imports) {
         // Validation and linking other than Wasm::ExternalKind::Function is already done in JSWebAssemblyInstance.
         // Eventually we will move all the linking code in JSWebAssemblyInstance here and remove this switch statement.
@@ -264,20 +262,18 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj
         }
 
         case Wasm::ExternalKind::Table: {
-            RELEASE_ASSERT(!hasTableImport); // This should be guaranteed by a validation failure.
             // 7. Otherwise (i is a table import):
-            hasTableImport = true;
             JSWebAssemblyTable* table = jsDynamicCast<JSWebAssemblyTable*>(vm, value);
             // i. If v is not a WebAssembly.Table object, throw a WebAssembly.LinkError.
             if (!table)
                 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "is not an instance of WebAssembly.Table")));
 
-            uint32_t expectedInitial = moduleInformation.tableInformation.initial();
+            uint32_t expectedInitial = moduleInformation.tables[import.kindIndex].initial();
             uint32_t actualInitial = table->length();
             if (actualInitial < expectedInitial)
                 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "provided an 'initial' that is too small")));
 
-            if (Optional<uint32_t> expectedMaximum = moduleInformation.tableInformation.maximum()) {
+            if (Optional<uint32_t> expectedMaximum = moduleInformation.tables[import.kindIndex].maximum()) {
                 Optional<uint32_t> actualMaximum = table->maximum();
                 if (!actualMaximum)
                     return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "does not have a 'maximum' but the module requires that it does")));
@@ -285,14 +281,14 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj
                     return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Imported Table", "'maximum' is larger than the module's expected 'maximum'")));
             }
 
-            auto expectedType = moduleInformation.tableInformation.type();
+            auto expectedType = moduleInformation.tables[import.kindIndex].type();
             auto actualType = table->table()->type();
             if (expectedType != actualType)
                 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "provided a 'type' that is wrong")));
 
             // ii. Append v to tables.
             // iii. Append v.[[Table]] to imports.
-            m_instance->setTable(vm, table);
+            m_instance->setTable(vm, import.kindIndex, table);
             RETURN_IF_EXCEPTION(scope, void());
             break;
         }
@@ -302,16 +298,16 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj
         }
     }
 
-    {
-        if (!!moduleInformation.tableInformation && moduleInformation.tableInformation.isImport()) {
+    for (unsigned i = 0; i < moduleInformation.tableCount(); ++i) {
+        if (moduleInformation.tables[i].isImport()) {
             // We should either have a Table import or we should have thrown an exception.
-            RELEASE_ASSERT(hasTableImport);
+            RELEASE_ASSERT(m_instance->table(i));
         }
 
-        if (!!moduleInformation.tableInformation && !hasTableImport) {
-            RELEASE_ASSERT(!moduleInformation.tableInformation.isImport());
+        if (!m_instance->table(i)) {
+            RELEASE_ASSERT(!moduleInformation.tables[i].isImport());
             // We create a Table when it's a Table definition.
-            RefPtr<Wasm::Table> wasmTable = Wasm::Table::tryCreate(moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum(), moduleInformation.tableInformation.type());
+            RefPtr<Wasm::Table> wasmTable = Wasm::Table::tryCreate(moduleInformation.tables[i].initial(), moduleInformation.tables[i].maximum(), moduleInformation.tables[i].type());
             if (!wasmTable)
                 return exception(createJSWebAssemblyLinkError(exec, vm, "couldn't create Table"));
             JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, globalObject->webAssemblyTableStructure(), wasmTable.releaseNonNull());
@@ -319,7 +315,7 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj
             // If it's defined to be too large, we should have thrown a validation error.
             scope.assertNoException();
             ASSERT(table);
-            m_instance->setTable(vm, table);
+            m_instance->setTable(vm, i, table);
             RETURN_IF_EXCEPTION(scope, void());
         }
     }
@@ -397,10 +393,8 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj
         }
         case Wasm::ExternalKind::Table: {
             // This should be guaranteed by module verification.
-            RELEASE_ASSERT(m_instance->table()); 
-            ASSERT(exp.kindIndex == 0);
-
-            exportedValue = m_instance->table();
+            RELEASE_ASSERT(m_instance->table(exp.kindIndex));
+            exportedValue = m_instance->table(exp.kindIndex);
             break;
         }
         case Wasm::ExternalKind::Memory: {
@@ -486,7 +480,6 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec)
     Wasm::Module& module = m_instance->instance().module();
     Wasm::CodeBlock* codeBlock = m_instance->instance().codeBlock();
     const Wasm::ModuleInformation& moduleInformation = module.moduleInformation();
-    JSWebAssemblyTable* table = m_instance->table();
 
     const Vector<Wasm::Segment::Ptr>& data = moduleInformation.data;
     
@@ -498,16 +491,16 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec)
             // Also, it could be that a table wasn't imported, or that the table
             // imported wasn't compatible. However, those should error out before
             // getting here.
-            ASSERT(!!table);
+            ASSERT(!!m_instance->table(element.tableIndex));
 
             if (!element.functionIndices.size())
                 continue;
 
-            uint32_t tableIndex = element.offset.isGlobalImport()
+            uint32_t elementIndex = element.offset.isGlobalImport()
                 ? static_cast<uint32_t>(m_instance->instance().loadI32Global(element.offset.globalImportIndex()))
                 : element.offset.constValue();
 
-            fn(element, tableIndex);
+            fn(element, element.tableIndex, elementIndex);
 
             if (exception)
                 break;
@@ -531,9 +524,9 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec)
     };
 
     // Validation of all element ranges comes before all Table and Memory initialization.
-    forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex) {
-        uint64_t lastWrittenIndex = static_cast<uint64_t>(tableIndex) + static_cast<uint64_t>(element.functionIndices.size()) - 1;
-        if (UNLIKELY(lastWrittenIndex >= table->length()))
+    forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex, uint32_t elementIndex) {
+        uint64_t lastWrittenIndex = static_cast<uint64_t>(elementIndex) + static_cast<uint64_t>(element.functionIndices.size()) - 1;
+        if (UNLIKELY(lastWrittenIndex >= m_instance->table(tableIndex)->length()))
             exception = JSValue(throwException(exec, scope, createJSWebAssemblyLinkError(exec, vm, "Element is trying to set an out of bounds table index"_s)));
     });
 
@@ -552,7 +545,7 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec)
         return exception.value();
 
     JSGlobalObject* globalObject = m_instance->globalObject(vm);
-    forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex) {
+    forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex, uint32_t elementIndex) {
         for (uint32_t i = 0; i < element.functionIndices.size(); ++i) {
             // FIXME: This essentially means we're exporting an import.
             // We need a story here. We need to create a WebAssemblyFunction
@@ -568,14 +561,14 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec)
                     // Because a WebAssemblyWrapperFunction can never wrap another WebAssemblyWrapperFunction,
                     // the only type this could be is WebAssemblyFunction.
                     RELEASE_ASSERT(wasmFunction);
-                    table->set(tableIndex, wasmFunction);
-                    ++tableIndex;
+                    m_instance->table(tableIndex)->set(elementIndex, wasmFunction);
+                    ++elementIndex;
                     continue;
                 }
 
-                table->set(tableIndex,
+                m_instance->table(tableIndex)->set(elementIndex,
                     WebAssemblyWrapperFunction::create(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), functionImport, functionIndex, m_instance.get(), signatureIndex));
-                ++tableIndex;
+                ++elementIndex;
                 continue;
             }
 
@@ -589,8 +582,8 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec)
             WebAssemblyFunction* function = WebAssemblyFunction::create(
                 vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), String(), m_instance.get(), embedderEntrypointCallee, entrypointLoadLocation, signatureIndex);
 
-            table->set(tableIndex, function);
-            ++tableIndex;
+            m_instance->table(tableIndex)->set(elementIndex, function);
+            ++elementIndex;
         }
     });
 
index 75285d6..96e8a1d 100644 (file)
         "tee_local":           { "category": "special",    "value":  34, "return": ["any"],      "parameter": ["any"],                  "immediate": [{"name": "local_index",    "type": "varuint32"}],                                            "description": "write a local variable or parameter and return the same value" },
         "get_global":          { "category": "special",    "value":  35, "return": ["any"],      "parameter": [],                       "immediate": [{"name": "global_index",   "type": "varuint32"}],                                            "description": "read a global variable" },
         "set_global":          { "category": "special",    "value":  36, "return": [],           "parameter": ["any"],                  "immediate": [{"name": "global_index",   "type": "varuint32"}],                                            "description": "write a global variable" },
-        "table.get":           { "category": "special",    "value":  37, "return": ["anyref"],   "parameter": ["i32"],                  "immediate": [],                                                                                           "description": "get a table value" },
-        "table.set":           { "category": "special",    "value":  38, "return": [],           "parameter": ["i32", "anyref"],        "immediate": [],                                                                                           "description": "set a table value" },
+        "table.get":           { "category": "special",    "value":  37, "return": ["anyref"],   "parameter": ["i32"],                  "immediate": [{"name": "table_index",    "type": "varuint32"}],                                            "description": "get a table value" },
+        "table.set":           { "category": "special",    "value":  38, "return": [],           "parameter": ["i32", "anyref"],        "immediate": [{"name": "table_index",    "type": "varuint32"}],                                            "description": "set a table value" },
         "call":                { "category": "call",       "value":  16, "return": ["call"],     "parameter": ["call"],                 "immediate": [{"name": "function_index", "type": "varuint32"}],                                            "description": "call a function by its index" },
-        "call_indirect":       { "category": "call",       "value":  17, "return": ["call"],     "parameter": ["call"],                 "immediate": [{"name": "type_index",     "type": "varuint32"}, {"name": "reserved", "type": "varuint1"}],  "description": "call a function indirect with an expected signature" },
+        "call_indirect":       { "category": "call",       "value":  17, "return": ["call"],     "parameter": ["call"],                 "immediate": [{"name": "type_index",     "type": "varuint32"}, {"name": "table_index","type": "varuint32"}],"description": "call a function indirect with an expected signature" },
         "i32.load8_s":         { "category": "memory",     "value":  44, "return": ["i32"],      "parameter": ["addr"],                 "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset",   "type": "varuint32"}], "description": "load from memory" },
         "i32.load8_u":         { "category": "memory",     "value":  45, "return": ["i32"],      "parameter": ["addr"],                 "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset",   "type": "varuint32"}], "description": "load from memory" },
         "i32.load16_s":        { "category": "memory",     "value":  46, "return": ["i32"],      "parameter": ["addr"],                 "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset",   "type": "varuint32"}], "description": "load from memory" },