WebAssembly JS API: implement importing and defining Memory
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 9 Dec 2016 22:38:39 +0000 (22:38 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 9 Dec 2016 22:38:39 +0000 (22:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164134

Reviewed by Keith Miller.

JSTests:

* wasm/Builder.js:
(const._importMemoryContinuation.section):
(const._importMemoryContinuation.assert):
(const._importMemoryContinuation):
(const._exportFunctionContinuation.const): Deleted.
(const._exportFunctionContinuation): Deleted.
* wasm/Builder_WebAssemblyBinary.js:
(const.emitters.Import):
* wasm/js-api/test_basic_api.js:
(const.c.in.constructorProperties.switch):
* wasm/js-api/test_memory.js: Added.
(assert):
(binaryShouldNotParse):
(test):
(test.testMemImportError):
* wasm/js-api/test_memory_constructor.js: Added.
(assert):
(throw.new.Error):
(testInvalidSize):
(assert.testInvalidInitial):
(testInvalidInitial.testInvalidMaximum):
(testInvalidInitial):
(testInvalidMaximum):
* wasm/self-test/test_BuilderJSON.js:

Source/JavaScriptCore:

This patch implements the WebAssembly.Memory object. It refactors
the code to now associate a Memory with the instance instead of
the Module.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* jsc.cpp:
(functionTestWasmModuleFunctions):
* runtime/VM.h:
* shell/CMakeLists.txt:
* testWasm.cpp: Removed.
This has bitrotted. I'm removing it.

* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::B3IRGenerator):
(JSC::Wasm::sizeOfLoadOp):
(JSC::Wasm::createJSToWasmWrapper):
(JSC::Wasm::parseAndCompile):
* wasm/WasmB3IRGenerator.h:
* wasm/WasmFormat.cpp:
(JSC::Wasm::ModuleInformation::~ModuleInformation): Deleted.
* wasm/WasmFormat.h:
* wasm/WasmMemory.cpp:
(JSC::Wasm::Memory::Memory):
* wasm/WasmMemory.h:
(JSC::Wasm::Memory::size):
(JSC::Wasm::Memory::initial):
(JSC::Wasm::Memory::maximum):
(JSC::Wasm::Memory::pinnedRegisters): Deleted.
* wasm/WasmMemoryInformation.cpp: Added.
(JSC::Wasm::MemoryInformation::MemoryInformation):
* wasm/WasmMemoryInformation.h: Added.
(JSC::Wasm::MemoryInformation::MemoryInformation):
(JSC::Wasm::MemoryInformation::pinnedRegisters):
(JSC::Wasm::MemoryInformation::initial):
(JSC::Wasm::MemoryInformation::maximum):
(JSC::Wasm::MemoryInformation::isImport):
(JSC::Wasm::MemoryInformation::operator bool):
* wasm/WasmModuleParser.cpp:
(JSC::Wasm::ModuleParser::parseImport):
(JSC::Wasm::ModuleParser::parseMemoryHelper):
(JSC::Wasm::ModuleParser::parseMemory):
(JSC::Wasm::ModuleParser::parseExport):
* wasm/WasmModuleParser.h:
* wasm/WasmPageCount.h: Added. Implement a new way of describing Wasm
pages and then asking for how many bytes a quantity of pages is. This
class also makes it clear when we're talking about bytes or pages.

(JSC::Wasm::PageCount::PageCount):
(JSC::Wasm::PageCount::bytes):
(JSC::Wasm::PageCount::isValid):
(JSC::Wasm::PageCount::max):
(JSC::Wasm::PageCount::operator bool):
(JSC::Wasm::PageCount::operator<):
(JSC::Wasm::PageCount::operator>):
(JSC::Wasm::PageCount::operator>=):
* wasm/WasmPlan.cpp:
(JSC::Wasm::Plan::run):
* wasm/WasmPlan.h:
(JSC::Wasm::Plan::memory): Deleted.
* wasm/WasmValidate.cpp:
(JSC::Wasm::Validate::hasMemory):
(JSC::Wasm::Validate::Validate):
(JSC::Wasm::validateFunction):
* wasm/WasmValidate.h:
* wasm/generateWasmValidateInlinesHeader.py:
* wasm/js/JSWebAssemblyInstance.cpp:
(JSC::JSWebAssemblyInstance::visitChildren):
* wasm/js/JSWebAssemblyInstance.h:
(JSC::JSWebAssemblyInstance::memory):
(JSC::JSWebAssemblyInstance::setMemory):
(JSC::JSWebAssemblyInstance::offsetOfImportFunctions):
(JSC::JSWebAssemblyInstance::allocationSize):
* wasm/js/JSWebAssemblyMemory.cpp:
(JSC::JSWebAssemblyMemory::create):
(JSC::JSWebAssemblyMemory::JSWebAssemblyMemory):
(JSC::JSWebAssemblyMemory::buffer):
(JSC::JSWebAssemblyMemory::visitChildren):
* wasm/js/JSWebAssemblyMemory.h:
(JSC::JSWebAssemblyMemory::memory):
* wasm/js/WebAssemblyFunction.cpp:
(JSC::callWebAssemblyFunction):
* wasm/js/WebAssemblyInstanceConstructor.cpp:
Handle importing and creating of memory according
to the spec. This also does the needed validation
of making sure the memory defined in the module
is compatible with the imported memory.

(JSC::constructJSWebAssemblyInstance):
* wasm/js/WebAssemblyMemoryConstructor.cpp:
(JSC::constructJSWebAssemblyMemory):
(JSC::callJSWebAssemblyMemory):
* wasm/js/WebAssemblyMemoryPrototype.cpp:
(JSC::webAssemblyMemoryProtoFuncBuffer):
(JSC::WebAssemblyMemoryPrototype::create):
(JSC::WebAssemblyMemoryPrototype::finishCreation):
* wasm/js/WebAssemblyMemoryPrototype.h:
* wasm/js/WebAssemblyModuleRecord.cpp:
(JSC::WebAssemblyModuleRecord::finishCreation):
(JSC::WebAssemblyModuleRecord::link):

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

40 files changed:
JSTests/ChangeLog
JSTests/wasm/Builder.js
JSTests/wasm/Builder_WebAssemblyBinary.js
JSTests/wasm/js-api/test_basic_api.js
JSTests/wasm/js-api/test_memory.js [new file with mode: 0644]
JSTests/wasm/js-api/test_memory_constructor.js [new file with mode: 0644]
JSTests/wasm/self-test/test_BuilderJSON.js
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/shell/CMakeLists.txt
Source/JavaScriptCore/testWasm.cpp [deleted file]
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmB3IRGenerator.h
Source/JavaScriptCore/wasm/WasmFormat.cpp
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmMemory.cpp
Source/JavaScriptCore/wasm/WasmMemory.h
Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmMemoryInformation.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmModuleParser.cpp
Source/JavaScriptCore/wasm/WasmModuleParser.h
Source/JavaScriptCore/wasm/WasmPageCount.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmPlan.cpp
Source/JavaScriptCore/wasm/WasmPlan.h
Source/JavaScriptCore/wasm/WasmValidate.cpp
Source/JavaScriptCore/wasm/WasmValidate.h
Source/JavaScriptCore/wasm/generateWasmValidateInlinesHeader.py
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.h
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyMemoryConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyMemoryPrototype.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyMemoryPrototype.h
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp

index 2662300..907a1a4 100644 (file)
@@ -1,3 +1,35 @@
+2016-12-09  Saam Barati  <sbarati@apple.com>
+
+        WebAssembly JS API: implement importing and defining Memory
+        https://bugs.webkit.org/show_bug.cgi?id=164134
+
+        Reviewed by Keith Miller.
+
+        * wasm/Builder.js:
+        (const._importMemoryContinuation.section):
+        (const._importMemoryContinuation.assert):
+        (const._importMemoryContinuation):
+        (const._exportFunctionContinuation.const): Deleted.
+        (const._exportFunctionContinuation): Deleted.
+        * wasm/Builder_WebAssemblyBinary.js:
+        (const.emitters.Import):
+        * wasm/js-api/test_basic_api.js:
+        (const.c.in.constructorProperties.switch):
+        * wasm/js-api/test_memory.js: Added.
+        (assert):
+        (binaryShouldNotParse):
+        (test):
+        (test.testMemImportError):
+        * wasm/js-api/test_memory_constructor.js: Added.
+        (assert):
+        (throw.new.Error):
+        (testInvalidSize):
+        (assert.testInvalidInitial):
+        (testInvalidInitial.testInvalidMaximum):
+        (testInvalidInitial):
+        (testInvalidMaximum):
+        * wasm/self-test/test_BuilderJSON.js:
+
 2016-12-08  JF Bastien  <jfbastien@apple.com>
 
         WebAssembly: JSC::link* shouldn't need a CodeBlock
index cecb2c7..2684e93 100644 (file)
@@ -102,6 +102,15 @@ const _importFunctionContinuation = (builder, section, nextBuilder) => {
     };
 };
 
+const _importMemoryContinuation = (builder, section, nextBuilder) => {
+    return (module, field, memoryDescription) => {
+        assert.isString(module, `Import function module should be a string, got "${module}"`);
+        assert.isString(field, `Import function field should be a string, got "${field}"`);
+        section.data.push({module, field, kind: "Memory", memoryDescription});
+        return nextBuilder;
+    };
+};
+
 const _exportFunctionContinuation = (builder, section, nextBuilder) => {
     return (field, index, type) => {
         assert.isString(field, `Export function field should be a string, got "${field}"`);
@@ -110,6 +119,7 @@ const _exportFunctionContinuation = (builder, section, nextBuilder) => {
             // Exports can leave the type unspecified, letting the Code builder patch them up later.
             type = _maybeRegisterType(builder, type);
         }
+
         // We can't check much about "index" here because the Code section succeeds the Export section. More work is done at Code().End() time.
         switch (typeof(index)) {
         case "string": break; // Assume it's a function name which will be revealed in the Code section.
@@ -125,6 +135,7 @@ const _exportFunctionContinuation = (builder, section, nextBuilder) => {
             break;
         default: throw new Error(`Export section's index must be a string or a number, got ${index}`);
         }
+
         const correspondingImport = builder._getFunctionFromIndexSpace(index);
         const importSection = builder._getSection("Import");
         if (typeof(index) === "object") {
@@ -368,10 +379,10 @@ export default class Builder {
                     const importBuilder = {
                         End: () => this,
                         Table: () => { throw new Error(`Unimplemented: import table`); },
-                        Memory: () => { throw new Error(`Unimplemented: import memory`); },
                         Global: () => { throw new Error(`Unimplemented: import global`); },
                     };
                     importBuilder.Function = _importFunctionContinuation(this, s, importBuilder);
+                    importBuilder.Memory = _importMemoryContinuation(this, s, importBuilder);
                     return importBuilder;
                 };
                 break;
@@ -465,7 +476,13 @@ export default class Builder {
                                     }
                                     if (typeof(e.type) === "undefined") {
                                         // This must be a function export from the Code section (re-exports were handled earlier).
-                                        const functionIndexSpaceOffset = importSection ? importSection.data.length : 0;
+                                        let functionIndexSpaceOffset = 0;
+                                        if (importSection) {
+                                            for (const {kind} of importSection.data) {
+                                                if (kind === "Function")
+                                                    ++functionIndexSpaceOffset;
+                                            }
+                                        }
                                         const functionIndex = e.index - functionIndexSpaceOffset;
                                         e.type = codeSection.data[functionIndex].type;
                                     }
index 494641a..b19b8b6 100644 (file)
@@ -53,9 +53,29 @@ const emitters = {
             put(bin, "uint8", WASM.externalKindValue[entry.kind]);
             switch (entry.kind) {
             default: throw new Error(`Implementation problem: unexpected kind ${entry.kind}`);
-            case "Function": put(bin, "varuint32", entry.type); break;
+            case "Function": {
+                put(bin, "varuint32", entry.type);
+                break;
+            }
             case "Table": throw new Error(`Not yet implemented`);
-            case "Memory": throw new Error(`Not yet implemented`);
+            case "Memory": {
+                let {initial, maximum} = entry.memoryDescription;
+                assert.truthy(typeof initial === "number", "We expect 'initial' to be a number");
+                initial |= 0;
+                let hasMaximum = 0;
+                if (typeof maximum === "number") {
+                    maximum |= 0;
+                    hasMaximum = 1;
+                } else {
+                    assert.truthy(typeof maximum === "undefined", "We expect 'maximum' to be a number if it's defined");
+                }
+
+                put(bin, "varuint1", hasMaximum);
+                put(bin, "varuint32", initial);
+                if (hasMaximum)
+                    put(bin, "varuint32", maximum);
+                break;
+            };
             case "Global": throw new Error(`Not yet implemented`);
             }
         }
index 3f7d0bd..9560036 100644 (file)
@@ -75,8 +75,7 @@ for (const c in constructorProperties) {
         // assert.eq(Symbol.toStringTag in instance.exports, true);
         break;
     case "Memory":
-        // FIXME Implement and test these APIs further. For now they just throw. https://bugs.webkit.org/show_bug.cgi?id=159775
-        assert.throws(() => new WebAssembly[c](), Error, `WebAssembly doesn't yet implement the ${c} constructor property`);
+        new WebAssembly.Memory({initial: 20});
         break;
     case "Table":
         // FIXME Implement and test these APIs further. For now they just throw. https://bugs.webkit.org/show_bug.cgi?id=159775
diff --git a/JSTests/wasm/js-api/test_memory.js b/JSTests/wasm/js-api/test_memory.js
new file mode 100644 (file)
index 0000000..c51a559
--- /dev/null
@@ -0,0 +1,385 @@
+// FIXME: use the assert library: https://bugs.webkit.org/show_bug.cgi?id=165684
+import Builder from '../Builder.js';
+
+function assert(b) {
+    if (!b) {
+        throw new Error("Bad assertion");
+    }
+}
+
+const pageSize = 64 * 1024;
+const maxPageCount = (2**32) / pageSize;
+
+function binaryShouldNotParse(builder) {
+    const bin = builder.WebAssembly().get();
+    let threw = false;
+    try {
+        const module = new WebAssembly.Module(bin);
+    } catch(e) {
+        threw = true;
+    }
+    assert(threw);
+}
+
+{
+    // Can't declare more than one memory.
+    const builder = (new Builder())
+        .Type().End()
+        .Import().Memory("imp", "memory", {initial: 20}).End()
+        .Function().End()
+        .Memory().InitialMaxPages(1, 1).End()
+        .Export().End()
+        .Code()
+        .End();
+    binaryShouldNotParse(builder);
+}
+
+{
+    // Can't declare more than one memory.
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Memory("imp", "memory", {initial: 20})
+            .Memory("imp", "memory", {initial: 30})
+        .End()
+        .Function().End()
+        .Export().End()
+        .Code()
+        .End();
+    binaryShouldNotParse(builder);
+}
+
+{
+    // initial must be <= maximum.
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Memory("imp", "memory", {initial: 20, maximum: 19})
+        .End()
+        .Function().End()
+        .Export().End()
+        .Code()
+        .End();
+    binaryShouldNotParse(builder);
+}
+
+{
+    // No loads when no memory defined.
+    const builder = (new Builder())
+        .Type().End()
+        .Import().End()
+        .Function().End()
+        .Export().Function("foo").End()
+        .Code()
+            .Function("foo", { params: ["i32"], ret: "i32" })
+                .GetLocal(0)
+                .I32Load(2, 0)
+                .Return()
+            .End()
+        .End();
+
+    binaryShouldNotParse(builder);
+}
+
+{
+    // No stores when no memory defined.
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+        .End()
+        .Function().End()
+        .Export().Function("foo").End()
+        .Code()
+            .Function("foo", { params: ["i32"] })
+                .GetLocal(0)
+                .GetLocal(0)
+                .I32Store(2, 0)
+                .Return()
+            .End()
+        .End();
+
+    binaryShouldNotParse(builder);
+}
+
+{
+    let threw = false;
+    try {
+        new WebAssembly.Memory(20);
+    } catch(e) {
+        assert(e instanceof TypeError);
+        assert(e.message === "WebAssembly.Memory expects its first argument to be an object");
+        threw = true;
+    }
+    assert(threw);
+}
+
+{
+    let threw = false;
+    try {
+        new WebAssembly.Memory({}, {});
+    } catch(e) {
+        assert(e instanceof TypeError);
+        assert(e.message === "WebAssembly.Memory expects exactly one argument");
+        threw = true;
+    }
+    assert(threw);
+}
+
+function test(f) {
+    noInline(f);
+    for (let i = 0; i < 2; i++) {
+        f(i);
+    }
+}
+
+test(function() {
+    const memoryDescription = {initial: 20, maximum: 20};
+    const builder = (new Builder())
+        .Type().End()
+        .Import().Memory("imp", "memory", memoryDescription).End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", { params: ["i32", "i32"], ret: "i32" })
+                .GetLocal(1)
+                .GetLocal(0)
+                .I32Store(2, 0)
+                .GetLocal(1)
+                .I32Load(2, 0)
+                .Return()
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const memory = new WebAssembly.Memory(memoryDescription);
+    const instance = new WebAssembly.Instance(module, { imp: { memory: memory } });
+    const foo = instance.exports.foo;
+    // foo(value, address)
+
+    const bytes = memoryDescription.initial * pageSize;
+    for (let i = 0; i < (bytes/4); i++) {
+        let value = i + 1;
+        let address = i * 4;
+        let result = foo(value, address);
+        assert(result === value);
+        let arrayBuffer = memory.buffer;
+        let buffer = new Uint32Array(arrayBuffer);
+        assert(buffer[i] === value);
+    }
+});
+
+test(function() {
+    const memoryDescription = {initial: 20, maximum: 20};
+    const builder = (new Builder())
+        .Type().End()
+        .Import().Memory("imp", "memory", memoryDescription).End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", { params: ["i32", "i32"], ret: "i32"})
+                .GetLocal(1)
+                .GetLocal(0)
+                .I32Store8(0, 0)
+                .GetLocal(1)
+                .I32Load8U(0, 0)
+                .Return()
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const memory = new WebAssembly.Memory(memoryDescription);
+    const instance = new WebAssembly.Instance(module, { imp: { memory: memory } });
+    const foo = instance.exports.foo;
+    // foo(value, address)
+
+    const bytes = memoryDescription.initial * pageSize;
+    for (let i = 0; i < bytes; i++) {
+        let value = (i + 1);
+        let address = i;
+        let result = foo(value, address);
+        let expectedValue = (value & ((2**8) - 1)); 
+        assert(result === expectedValue);
+        let arrayBuffer = memory.buffer;
+        let buffer = new Uint8Array(arrayBuffer);
+        assert(buffer[i] === expectedValue);
+    }
+});
+
+test(function() {
+    const memoryDescription = {initial: 20, maximum: 20};
+    const builder = (new Builder())
+        .Type().End()
+        .Import().Memory("imp", "memory", memoryDescription).End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", { params: ["f32", "i32"], ret: "f32"})
+                .GetLocal(1)
+                .GetLocal(0)
+                .F32Store(2, 0)
+                .GetLocal(1)
+                .F32Load(2, 0)
+                .Return()
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const memory = new WebAssembly.Memory(memoryDescription);
+    const instance = new WebAssembly.Instance(module, { imp: { memory: memory } });
+    const foo = instance.exports.foo;
+    // foo(value, address)
+
+    const bytes = memoryDescription.initial * pageSize;
+    for (let i = 0; i < (bytes/4); i++) {
+        let value = i + 1 + .0128213781289;
+        assert(value !== Math.fround(value));
+        let address = i * 4;
+        let result = foo(value, address);
+        let expectedValue = Math.fround(result);
+        assert(result === expectedValue);
+        let arrayBuffer = memory.buffer;
+        let buffer = new Float32Array(arrayBuffer);
+        assert(buffer[i] === expectedValue);
+    }
+});
+
+test(function() {
+    const memoryDescription = {initial: 20, maximum: 20};
+    const builder = (new Builder())
+        .Type().End()
+        .Import().Memory("imp", "memory", memoryDescription).End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", { params: ["f64", "i32"], ret: "f64"})
+                .GetLocal(1)
+                .GetLocal(0)
+                .F64Store(3, 0)
+                .GetLocal(1)
+                .F64Load(3, 0)
+                .Return()
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const memory = new WebAssembly.Memory(memoryDescription);
+    const instance = new WebAssembly.Instance(module, { imp: { memory: memory } });
+    const foo = instance.exports.foo;
+    // foo(value, address)
+
+    const bytes = memoryDescription.initial * pageSize;
+    for (let i = 0; i < (bytes/8); i++) {
+        let value = i + 1 + .0128213781289;
+        let address = i * 8;
+        let result = foo(value, address);
+        let expectedValue = result;
+        assert(result === expectedValue);
+        let arrayBuffer = memory.buffer;
+        let buffer = new Float64Array(arrayBuffer);
+        assert(buffer[i] === expectedValue);
+    }
+});
+
+test(function() {
+    const memoryDescription = {initial: 20, maximum: 25};
+    const builder = (new Builder())
+        .Type().End()
+        .Import().Memory("imp", "memory", memoryDescription).End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", { params: ["f64", "i32"], ret: "f64"})
+                .GetLocal(1)
+                .GetLocal(0)
+                .F64Store(3, 0)
+                .GetLocal(1)
+                .F64Load(3, 0)
+                .Return()
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+
+    function testMemImportError(instanceObj, expectedError) {
+        let threw = false;
+        try {
+            new WebAssembly.Instance(module, instanceObj);
+        } catch(e) {
+            assert(e instanceof TypeError);
+            threw = true;
+            if (expectedError) {
+                assert(e.message === expectedError);
+            }
+        }
+        assert(threw);
+    }
+
+    testMemImportError(20);
+    testMemImportError({ });
+    testMemImportError({imp: { } });
+    testMemImportError({imp: { memory: 20 } });
+    testMemImportError({imp: { memory: [] } });
+    testMemImportError({imp: { memory: new WebAssembly.Memory({initial: 19, maximum: 25}) } }, "Memory import provided an 'initial' that is too small");
+    testMemImportError({imp: { memory: new WebAssembly.Memory({initial: 20}) } }, "Memory import did not have a 'maximum' but the module requires that it does");
+    testMemImportError({imp: { memory: new WebAssembly.Memory({initial: 20, maximum: 26}) } }, "Memory imports 'maximum' is larger than the module's expected 'maximum");
+});
+
+test(function() {
+    const memoryDescription = {initial: 20};
+    const builder = (new Builder())
+        .Type().End()
+        .Import().Memory("imp", "memory", memoryDescription).End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", { params: ["f64", "i32"], ret: "f64"})
+                .GetLocal(1)
+                .GetLocal(0)
+                .F64Store(3, 0)
+                .GetLocal(1)
+                .F64Load(3, 0)
+                .Return()
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+
+    function testMemImportError(instanceObj, expectedError) {
+        let threw = false;
+        try {
+            new WebAssembly.Instance(module, instanceObj);
+        } catch(e) {
+            assert(e instanceof TypeError);
+            threw = true;
+            if (expectedError) {
+                assert(e.message === expectedError);
+            }
+        }
+        assert(threw);
+    }
+
+    testMemImportError({imp: { memory: new WebAssembly.Memory({initial: 19, maximum: 25}) } }, "Memory import provided an 'initial' that is too small");
+    testMemImportError({imp: { memory: new WebAssembly.Memory({initial: 19}) } }, "Memory import provided an 'initial' that is too small");
+
+    // This should not throw.
+    new WebAssembly.Instance(module, {imp: {memory: new WebAssembly.Memory({initial:20})}});
+    new WebAssembly.Instance(module, {imp: {memory: new WebAssembly.Memory({initial:20, maximum:20})}});
+});
diff --git a/JSTests/wasm/js-api/test_memory_constructor.js b/JSTests/wasm/js-api/test_memory_constructor.js
new file mode 100644 (file)
index 0000000..96c938f
--- /dev/null
@@ -0,0 +1,79 @@
+// FIXME: use the assert library: https://bugs.webkit.org/show_bug.cgi?id=165684
+import Builder from '../Builder.js';
+
+function assert(b) {
+    if (!b) {
+        throw new Error("Bad assertion");
+    }
+}
+
+{
+    let threw = false;
+    try {
+        new WebAssembly.Memory({initial: 20, maximum: 19});
+    } catch(e) {
+        assert(e instanceof RangeError);
+        assert(e.message === "'maximum' page count must be than greater than or equal to the 'initial' page count");
+        threw = true;
+    }
+    assert(threw);
+}
+
+const pageSize = 64 * 1024;
+const maxPageCount = (2**32) / pageSize;
+
+function testInvalidSize(description, propName) {
+    let threw = false;
+    try {
+        new WebAssembly.Memory(description);
+    } catch(e) {
+        threw = true;
+        assert(e instanceof RangeError);
+        assert(e.message === `WebAssembly.Memory '${propName}' page count is too large`);
+    }
+    assert(threw);
+}
+
+{
+    function testInvalidInitial(v) {
+        testInvalidSize({initial: v}, "initial");
+    }
+
+    // These should not throw.
+    new WebAssembly.Memory({initial: maxPageCount});
+    new WebAssembly.Memory({initial: maxPageCount, maximum: maxPageCount});
+
+    testInvalidInitial(2**31);
+    testInvalidInitial(maxPageCount + 1);
+}
+
+{
+    function testInvalidMaximum(v) {
+        testInvalidSize({initial: 1, maximum: v}, "maximum");
+    }
+
+    testInvalidMaximum(2**31);
+    testInvalidMaximum(maxPageCount + 1);
+}
+
+{
+    for (let i = 0; i < 5; i++) {
+        let x = Math.random() * (2**10);
+        x |= 0;
+        const mem = new WebAssembly.Memory({initial: x, maximum: x + 100});
+        assert(mem.buffer.byteLength === x * pageSize);
+    }
+}
+
+{
+    let bufferGetter = Object.getOwnPropertyDescriptor((new WebAssembly.Memory({initial:1})).__proto__, "buffer").get;
+    let threw = false;
+    try {
+        bufferGetter.call({});
+    } catch(e) {
+        assert(e instanceof TypeError);
+        assert(e.message === "WebAssembly.Memory.prototype.buffer getter called with non WebAssembly.Memory |this| value");
+        threw = true;
+    }
+    assert(threw);
+}
index 731d262..055aaf6 100644 (file)
@@ -569,4 +569,23 @@ const assertOpThrows = (opFn, message) => {
     assert.eq(j.section[1].data[0].code[3].name, "select");
 })();
 
+(function MemoryImport() {
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Memory("__module__", "__field__", {initial: 30, maximum: 31})
+        .End()
+        .Code().End();
+
+    const json = JSON.parse(builder.json());
+    assert.eq(json.section.length, 3);
+    assert.eq(json.section[1].name, "Import");
+    assert.eq(json.section[1].data.length, 1);
+    assert.eq(json.section[1].data[0].module, "__module__");
+    assert.eq(json.section[1].data[0].field, "__field__");
+    assert.eq(json.section[1].data[0].kind, "Memory");
+    assert.eq(json.section[1].data[0].memoryDescription.initial, 30);
+    assert.eq(json.section[1].data[0].memoryDescription.maximum, 31);
+})();
+
 // FIXME test type mismatch with select. https://bugs.webkit.org/show_bug.cgi?id=163267
index d0fb3ab..e548380 100644 (file)
@@ -902,6 +902,7 @@ set(JavaScriptCore_SOURCES
     wasm/WasmCallingConvention.cpp
     wasm/WasmFormat.cpp
     wasm/WasmMemory.cpp
+    wasm/WasmMemoryInformation.cpp
     wasm/WasmModuleParser.cpp
     wasm/WasmPlan.cpp
     wasm/WasmValidate.cpp
index bf40657..3cb2d86 100644 (file)
@@ -1,3 +1,111 @@
+2016-12-09  Saam Barati  <sbarati@apple.com>
+
+        WebAssembly JS API: implement importing and defining Memory
+        https://bugs.webkit.org/show_bug.cgi?id=164134
+
+        Reviewed by Keith Miller.
+
+        This patch implements the WebAssembly.Memory object. It refactors
+        the code to now associate a Memory with the instance instead of
+        the Module.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * jsc.cpp:
+        (functionTestWasmModuleFunctions):
+        * runtime/VM.h:
+        * shell/CMakeLists.txt:
+        * testWasm.cpp: Removed.
+        This has bitrotted. I'm removing it.
+
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::B3IRGenerator):
+        (JSC::Wasm::sizeOfLoadOp):
+        (JSC::Wasm::createJSToWasmWrapper):
+        (JSC::Wasm::parseAndCompile):
+        * wasm/WasmB3IRGenerator.h:
+        * wasm/WasmFormat.cpp:
+        (JSC::Wasm::ModuleInformation::~ModuleInformation): Deleted.
+        * wasm/WasmFormat.h:
+        * wasm/WasmMemory.cpp:
+        (JSC::Wasm::Memory::Memory):
+        * wasm/WasmMemory.h:
+        (JSC::Wasm::Memory::size):
+        (JSC::Wasm::Memory::initial):
+        (JSC::Wasm::Memory::maximum):
+        (JSC::Wasm::Memory::pinnedRegisters): Deleted.
+        * wasm/WasmMemoryInformation.cpp: Added.
+        (JSC::Wasm::MemoryInformation::MemoryInformation):
+        * wasm/WasmMemoryInformation.h: Added.
+        (JSC::Wasm::MemoryInformation::MemoryInformation):
+        (JSC::Wasm::MemoryInformation::pinnedRegisters):
+        (JSC::Wasm::MemoryInformation::initial):
+        (JSC::Wasm::MemoryInformation::maximum):
+        (JSC::Wasm::MemoryInformation::isImport):
+        (JSC::Wasm::MemoryInformation::operator bool):
+        * wasm/WasmModuleParser.cpp:
+        (JSC::Wasm::ModuleParser::parseImport):
+        (JSC::Wasm::ModuleParser::parseMemoryHelper):
+        (JSC::Wasm::ModuleParser::parseMemory):
+        (JSC::Wasm::ModuleParser::parseExport):
+        * wasm/WasmModuleParser.h:
+        * wasm/WasmPageCount.h: Added. Implement a new way of describing Wasm
+        pages and then asking for how many bytes a quantity of pages is. This
+        class also makes it clear when we're talking about bytes or pages.
+
+        (JSC::Wasm::PageCount::PageCount):
+        (JSC::Wasm::PageCount::bytes):
+        (JSC::Wasm::PageCount::isValid):
+        (JSC::Wasm::PageCount::max):
+        (JSC::Wasm::PageCount::operator bool):
+        (JSC::Wasm::PageCount::operator<):
+        (JSC::Wasm::PageCount::operator>):
+        (JSC::Wasm::PageCount::operator>=):
+        * wasm/WasmPlan.cpp:
+        (JSC::Wasm::Plan::run):
+        * wasm/WasmPlan.h:
+        (JSC::Wasm::Plan::memory): Deleted.
+        * wasm/WasmValidate.cpp:
+        (JSC::Wasm::Validate::hasMemory):
+        (JSC::Wasm::Validate::Validate):
+        (JSC::Wasm::validateFunction):
+        * wasm/WasmValidate.h:
+        * wasm/generateWasmValidateInlinesHeader.py:
+        * wasm/js/JSWebAssemblyInstance.cpp:
+        (JSC::JSWebAssemblyInstance::visitChildren):
+        * wasm/js/JSWebAssemblyInstance.h:
+        (JSC::JSWebAssemblyInstance::memory):
+        (JSC::JSWebAssemblyInstance::setMemory):
+        (JSC::JSWebAssemblyInstance::offsetOfImportFunctions):
+        (JSC::JSWebAssemblyInstance::allocationSize):
+        * wasm/js/JSWebAssemblyMemory.cpp:
+        (JSC::JSWebAssemblyMemory::create):
+        (JSC::JSWebAssemblyMemory::JSWebAssemblyMemory):
+        (JSC::JSWebAssemblyMemory::buffer):
+        (JSC::JSWebAssemblyMemory::visitChildren):
+        * wasm/js/JSWebAssemblyMemory.h:
+        (JSC::JSWebAssemblyMemory::memory):
+        * wasm/js/WebAssemblyFunction.cpp:
+        (JSC::callWebAssemblyFunction):
+        * wasm/js/WebAssemblyInstanceConstructor.cpp:
+        Handle importing and creating of memory according
+        to the spec. This also does the needed validation
+        of making sure the memory defined in the module
+        is compatible with the imported memory.
+
+        (JSC::constructJSWebAssemblyInstance):
+        * wasm/js/WebAssemblyMemoryConstructor.cpp:
+        (JSC::constructJSWebAssemblyMemory):
+        (JSC::callJSWebAssemblyMemory):
+        * wasm/js/WebAssemblyMemoryPrototype.cpp:
+        (JSC::webAssemblyMemoryProtoFuncBuffer):
+        (JSC::WebAssemblyMemoryPrototype::create):
+        (JSC::WebAssemblyMemoryPrototype::finishCreation):
+        * wasm/js/WebAssemblyMemoryPrototype.h:
+        * wasm/js/WebAssemblyModuleRecord.cpp:
+        (JSC::WebAssemblyModuleRecord::finishCreation):
+        (JSC::WebAssemblyModuleRecord::link):
+
 2016-12-09  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Some resources fetched via Fetch API do not have data
index 3d05fa2..982bff7 100644 (file)
@@ -25,7 +25,6 @@
                        buildPhases = (
                        );
                        dependencies = (
-                               539EB0831D5560F400C82EF7 /* PBXTargetDependency */,
                                0F6183471C45F67A0072450B /* PBXTargetDependency */,
                                0F93275D1C20BF3A00CF6564 /* PBXTargetDependency */,
                                0FEC85B11BDB5D8F0080FF74 /* PBXTargetDependency */,
                5370B4F51BF26202005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5370B4F31BF25EA2005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.cpp */; };
                5370B4F61BF26205005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 5370B4F41BF25EA2005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.h */; };
                53917E7B1B7906FA000EBD33 /* JSGenericTypedArrayViewPrototypeFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 53917E7A1B7906E4000EBD33 /* JSGenericTypedArrayViewPrototypeFunctions.h */; };
-               539EB0791D55607000C82EF7 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51F0EB6105C86C6B00E6DF1B /* Foundation.framework */; };
-               539EB07A1D55607000C82EF7 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 932F5BD90822A1C700736975 /* JavaScriptCore.framework */; };
-               539EB0811D55608A00C82EF7 /* testWasm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 539EB0711D553DF800C82EF7 /* testWasm.cpp */; };
                539FB8BA1C99DA7C00940FA1 /* JSArrayInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 539FB8B91C99DA7C00940FA1 /* JSArrayInlines.h */; };
                53D444DC1DAF08AB00B92784 /* B3WasmAddressValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 53D444DB1DAF08AB00B92784 /* B3WasmAddressValue.h */; };
                53D444DE1DAF09A000B92784 /* B3WasmAddressValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53D444DD1DAF09A000B92784 /* B3WasmAddressValue.cpp */; };
                79B00CBE1C6AB07E0088C65D /* ProxyObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79B00CBA1C6AB07E0088C65D /* ProxyObject.cpp */; settings = {COMPILER_FLAGS = "-fno-optimize-sibling-calls"; }; };
                79B00CBF1C6AB07E0088C65D /* ProxyObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 79B00CBB1C6AB07E0088C65D /* ProxyObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
                79B1788E1D399B8000B1A567 /* JITMathICForwards.h in Headers */ = {isa = PBXBuildFile; fileRef = 79A899FE1D38612E00D18C73 /* JITMathICForwards.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               79B759741DFA4C600052174C /* WasmMemoryInformation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79B759711DFA4C600052174C /* WasmMemoryInformation.cpp */; };
+               79B759751DFA4C600052174C /* WasmMemoryInformation.h in Headers */ = {isa = PBXBuildFile; fileRef = 79B759721DFA4C600052174C /* WasmMemoryInformation.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               79B759761DFA4C600052174C /* WasmPageCount.h in Headers */ = {isa = PBXBuildFile; fileRef = 79B759731DFA4C600052174C /* WasmPageCount.h */; settings = {ATTRIBUTES = (Private, ); }; };
                79B819931DD25CF500DDC714 /* JSGlobalObjectInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 79B819921DD25CF500DDC714 /* JSGlobalObjectInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                79C4B15D1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B15B1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.cpp */; };
                79C4B15E1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 79C4B15C1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
                        remoteGlobalIDString = 0F4680A914BA7FD900BFE272;
                        remoteInfo = "LLInt Offsets";
                };
-               539EB0821D5560F400C82EF7 /* PBXContainerItemProxy */ = {
-                       isa = PBXContainerItemProxy;
-                       containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
-                       proxyType = 1;
-                       remoteGlobalIDString = 539EB0751D55607000C82EF7;
-                       remoteInfo = testWASM;
-               };
                5D69E911152BE5470028D720 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
                53917E7C1B791106000EBD33 /* JSTypedArrayViewPrototype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSTypedArrayViewPrototype.h; sourceTree = "<group>"; };
                53917E831B791CB8000EBD33 /* TypedArrayPrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = TypedArrayPrototype.js; path = builtins/TypedArrayPrototype.js; sourceTree = SOURCE_ROOT; };
                539EB0711D553DF800C82EF7 /* testWasm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testWasm.cpp; sourceTree = "<group>"; };
-               539EB0801D55607000C82EF7 /* testWASM */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testWASM; sourceTree = BUILT_PRODUCTS_DIR; };
                539FB8B91C99DA7C00940FA1 /* JSArrayInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSArrayInlines.h; sourceTree = "<group>"; };
                53D444DB1DAF08AB00B92784 /* B3WasmAddressValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3WasmAddressValue.h; path = b3/B3WasmAddressValue.h; sourceTree = "<group>"; };
                53D444DD1DAF09A000B92784 /* B3WasmAddressValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3WasmAddressValue.cpp; path = b3/B3WasmAddressValue.cpp; sourceTree = "<group>"; };
                79B00CB91C6AB07E0088C65D /* ProxyConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProxyConstructor.h; sourceTree = "<group>"; };
                79B00CBA1C6AB07E0088C65D /* ProxyObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProxyObject.cpp; sourceTree = "<group>"; };
                79B00CBB1C6AB07E0088C65D /* ProxyObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProxyObject.h; sourceTree = "<group>"; };
+               79B759711DFA4C600052174C /* WasmMemoryInformation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmMemoryInformation.cpp; sourceTree = "<group>"; };
+               79B759721DFA4C600052174C /* WasmMemoryInformation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmMemoryInformation.h; sourceTree = "<group>"; };
+               79B759731DFA4C600052174C /* WasmPageCount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmPageCount.h; sourceTree = "<group>"; };
                79B819921DD25CF500DDC714 /* JSGlobalObjectInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGlobalObjectInlines.h; sourceTree = "<group>"; };
                79C4B15B1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGLiveCatchVariablePreservationPhase.cpp; path = dfg/DFGLiveCatchVariablePreservationPhase.cpp; sourceTree = "<group>"; };
                79C4B15C1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGLiveCatchVariablePreservationPhase.h; path = dfg/DFGLiveCatchVariablePreservationPhase.h; sourceTree = "<group>"; };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
-               539EB0781D55607000C82EF7 /* Frameworks */ = {
-                       isa = PBXFrameworksBuildPhase;
-                       buildActionMask = 2147483647;
-                       files = (
-                               539EB0791D55607000C82EF7 /* Foundation.framework in Frameworks */,
-                               539EB07A1D55607000C82EF7 /* JavaScriptCore.framework in Frameworks */,
-                       );
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
                651122FC14046A4C002B101D /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                                6511230514046A4C002B101D /* testRegExp */,
                                0F9327591C20BCBA00CF6564 /* dynbench */,
                                0F6183431C45F62A0072450B /* testair */,
-                               539EB0801D55607000C82EF7 /* testWASM */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                                53F40E8A1D5901BB0099A1B6 /* WasmFunctionParser.h */,
                                535557151D9DFA32006D583B /* WasmMemory.cpp */,
                                535557131D9D9EA5006D583B /* WasmMemory.h */,
+                               79B759711DFA4C600052174C /* WasmMemoryInformation.cpp */,
+                               79B759721DFA4C600052174C /* WasmMemoryInformation.h */,
                                53F40E961D5A7BEC0099A1B6 /* WasmModuleParser.cpp */,
                                53F40E941D5A7AEF0099A1B6 /* WasmModuleParser.h */,
+                               79B759731DFA4C600052174C /* WasmPageCount.h */,
                                53F40E8C1D5901F20099A1B6 /* WasmParser.h */,
                                531374BE1D5CE95000AF7A0B /* WasmPlan.cpp */,
                                531374BC1D5CE67600AF7A0B /* WasmPlan.h */,
                                0F18D3D01B55A6E0002C5C9F /* DFGAdaptiveStructureWatchpoint.h in Headers */,
                                998ED6751BED768C00DD8017 /* RemoteControllableTarget.h in Headers */,
                                0F33FCF81C136E2500323F67 /* B3StackmapGenerationParams.h in Headers */,
+                               79B759761DFA4C600052174C /* WasmPageCount.h in Headers */,
                                0F66E16B14DF3F1600B7B2E4 /* DFGAdjacencyList.h in Headers */,
                                0FFB921816D02EB20055A5DB /* DFGAllocator.h in Headers */,
                                0F1E3A461534CBAF000F9456 /* DFGArgumentPosition.h in Headers */,
                                BC02E98D0E183E38000F9297 /* ErrorInstance.h in Headers */,
                                BC02E90F0E1839DB000F9297 /* ErrorPrototype.h in Headers */,
                                996B731B1BDA08D100331B84 /* ErrorPrototype.lut.h in Headers */,
+                               79B759751DFA4C600052174C /* WasmMemoryInformation.h in Headers */,
                                969A07980ED1D3AE00F1F681 /* DirectEvalCodeCache.h in Headers */,
                                A54982041891D0B00081E5B8 /* EventLoop.h in Headers */,
                                FE1C0FFD1B193E9800B53FCA /* Exception.h in Headers */,
                        productReference = 14BD59BF0A3E8F9000BAF59C /* testapi */;
                        productType = "com.apple.product-type.tool";
                };
-               539EB0751D55607000C82EF7 /* testWASM */ = {
-                       isa = PBXNativeTarget;
-                       buildConfigurationList = 539EB07B1D55607000C82EF7 /* Build configuration list for PBXNativeTarget "testWASM" */;
-                       buildPhases = (
-                               539EB0761D55607000C82EF7 /* Sources */,
-                               539EB0781D55607000C82EF7 /* Frameworks */,
-                       );
-                       buildRules = (
-                       );
-                       dependencies = (
-                       );
-                       name = testWASM;
-                       productName = testapi;
-                       productReference = 539EB0801D55607000C82EF7 /* testWASM */;
-                       productType = "com.apple.product-type.tool";
-               };
                651122F714046A4C002B101D /* testRegExp */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = 6511230014046A4C002B101D /* Build configuration list for PBXNativeTarget "testRegExp" */;
                                5D6B2A47152B9E17005231DE /* Test Tools */,
                                0F93274E1C20BCBA00CF6564 /* dynbench */,
                                0F6183381C45F62A0072450B /* testair */,
-                               539EB0751D55607000C82EF7 /* testWASM */,
                        );
                };
 /* End PBXProject section */
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
-               539EB0761D55607000C82EF7 /* Sources */ = {
-                       isa = PBXSourcesBuildPhase;
-                       buildActionMask = 2147483647;
-                       files = (
-                               539EB0811D55608A00C82EF7 /* testWasm.cpp in Sources */,
-                       );
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
                651122FA14046A4C002B101D /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                                C2FCAE1017A9C24E0034C735 /* BytecodeBasicBlock.cpp in Sources */,
                                148F21AA107EC53A0042EC2C /* BytecodeGenerator.cpp in Sources */,
                                7094C4DE1AE439530041A2EE /* BytecodeIntrinsicRegistry.cpp in Sources */,
+                               79B759741DFA4C600052174C /* WasmMemoryInformation.cpp in Sources */,
                                C2FCAE1217A9C24E0034C735 /* BytecodeLivenessAnalysis.cpp in Sources */,
                                0F338E0D1BF0276C0013C88F /* B3DataSection.cpp in Sources */,
                                65B8392F1BACAD6A0044E824 /* CachedRecovery.cpp in Sources */,
                        target = 0F4680A914BA7FD900BFE272 /* LLInt Offsets */;
                        targetProxy = 0FF922D214F46B2F0041A24E /* PBXContainerItemProxy */;
                };
-               539EB0831D5560F400C82EF7 /* PBXTargetDependency */ = {
-                       isa = PBXTargetDependency;
-                       target = 539EB0751D55607000C82EF7 /* testWASM */;
-                       targetProxy = 539EB0821D5560F400C82EF7 /* PBXContainerItemProxy */;
-               };
                5D69E912152BE5470028D720 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = 932F5BDA0822A1C700736975 /* jsc */;
                        };
                        name = Production;
                };
-               539EB07C1D55607000C82EF7 /* Debug */ = {
-                       isa = XCBuildConfiguration;
-                       baseConfigurationReference = BC021BF2136900C300FC5467 /* ToolExecutable.xcconfig */;
-                       buildSettings = {
-                               PRODUCT_NAME = "$(TARGET_NAME)";
-                       };
-                       name = Debug;
-               };
-               539EB07D1D55607000C82EF7 /* Release */ = {
-                       isa = XCBuildConfiguration;
-                       baseConfigurationReference = BC021BF2136900C300FC5467 /* ToolExecutable.xcconfig */;
-                       buildSettings = {
-                               PRODUCT_NAME = "$(TARGET_NAME)";
-                       };
-                       name = Release;
-               };
-               539EB07E1D55607000C82EF7 /* Profiling */ = {
-                       isa = XCBuildConfiguration;
-                       baseConfigurationReference = BC021BF2136900C300FC5467 /* ToolExecutable.xcconfig */;
-                       buildSettings = {
-                               PRODUCT_NAME = "$(TARGET_NAME)";
-                       };
-                       name = Profiling;
-               };
-               539EB07F1D55607000C82EF7 /* Production */ = {
-                       isa = XCBuildConfiguration;
-                       baseConfigurationReference = BC021BF2136900C300FC5467 /* ToolExecutable.xcconfig */;
-                       buildSettings = {
-                               PRODUCT_NAME = "$(TARGET_NAME)";
-                       };
-                       name = Production;
-               };
                5D6B2A48152B9E17005231DE /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Production;
                };
-               539EB07B1D55607000C82EF7 /* Build configuration list for PBXNativeTarget "testWASM" */ = {
-                       isa = XCConfigurationList;
-                       buildConfigurations = (
-                               539EB07C1D55607000C82EF7 /* Debug */,
-                               539EB07D1D55607000C82EF7 /* Release */,
-                               539EB07E1D55607000C82EF7 /* Profiling */,
-                               539EB07F1D55607000C82EF7 /* Production */,
-                       );
-                       defaultConfigurationIsVisible = 0;
-                       defaultConfigurationName = Production;
-               };
                5D6B2A4C152B9E17005231DE /* Build configuration list for PBXAggregateTarget "Test Tools" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
index f41c85f..940425c 100644 (file)
@@ -69,6 +69,7 @@
 #include "TestRunnerUtils.h"
 #include "TypeProfilerLog.h"
 #include "WasmPlan.h"
+#include "WasmMemory.h"
 #include <locale.h>
 #include <math.h>
 #include <stdio.h>
@@ -2632,6 +2633,19 @@ static EncodedJSValue JSC_HOST_CALL functionTestWasmModuleFunctions(ExecState* e
             });
     }
 
+    void* memoryBytes = nullptr;
+    uint32_t memorySize = 0;
+    std::unique_ptr<Wasm::Memory> memory;
+    std::unique_ptr<Wasm::ModuleInformation> moduleInformation = plan.takeModuleInformation();
+
+    if (!!moduleInformation->memory) {
+        memory = std::make_unique<Wasm::Memory>(moduleInformation->memory.initial(), moduleInformation->memory.maximum());
+        memoryBytes = memory->memory();
+        memorySize = memory->size();
+    }
+    vm.topWasmMemoryPointer = memoryBytes;
+    vm.topWasmMemorySize = memorySize;
+
     for (uint32_t i = 0; i < functionCount; ++i) {
         JSArray* testCases = jsCast<JSArray*>(exec->argument(i + 2));
         for (unsigned testIndex = 0; testIndex < testCases->length(); ++testIndex) {
index a02bb52..13b8dd2 100644 (file)
@@ -296,6 +296,8 @@ public:
     // https://bugs.webkit.org/show_bug.cgi?id=160441
     ExecState* topCallFrame;
     JSWebAssemblyInstance* topJSWebAssemblyInstance;
+    void* topWasmMemoryPointer;
+    uint32_t topWasmMemorySize;
     Strong<Structure> structureStructure;
     Strong<Structure> structureRareDataStructure;
     Strong<Structure> terminatedExecutionErrorStructure;
index 089cc20..49152bc 100644 (file)
@@ -49,17 +49,10 @@ if (NOT WIN32)
         ../b3/air/testair.cpp
     )
 
-    set(TESTWASM_SOURCES
-        ../testWasm.cpp
-    )
-
     add_executable(testb3 ${TESTB3_SOURCES})
     target_link_libraries(testb3 ${JSC_LIBRARIES})
 
     add_executable(testair ${TESTAIR_SOURCES})
     target_link_libraries(testair ${JSC_LIBRARIES})
 
-    add_executable(testWASM ${TESTWASM_SOURCES})
-    target_link_libraries(testWASM ${JSC_LIBRARIES})
-
 endif ()
diff --git a/Source/JavaScriptCore/testWasm.cpp b/Source/JavaScriptCore/testWasm.cpp
deleted file mode 100644 (file)
index 8f933c4..0000000
+++ /dev/null
@@ -1,1344 +0,0 @@
-/*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#include "B3Common.h"
-#include "B3Compilation.h"
-#include "InitializeThreading.h"
-#include "JSCJSValueInlines.h"
-#include "JSString.h"
-#include "LLIntThunks.h"
-#include "ProtoCallFrame.h"
-#include "VM.h"
-#include "WasmMemory.h"
-#include "WasmPlan.h"
-
-#include <wtf/DataLog.h>
-#include <wtf/LEBDecoder.h>
-
-class CommandLine {
-public:
-    CommandLine(int argc, char** argv)
-    {
-        parseArguments(argc, argv);
-    }
-
-    bool m_runWasmTests { false };
-
-    void parseArguments(int, char**);
-};
-
-static NO_RETURN void printUsageStatement(bool help = false)
-{
-    fprintf(stderr, "Usage: testWasm [options]\n");
-    fprintf(stderr, "  -h|--help  Prints this help message\n");
-    fprintf(stderr, "  -w|--web   Run the Wasm tests\n");
-    fprintf(stderr, "\n");
-
-    exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
-}
-
-void CommandLine::parseArguments(int argc, char** argv)
-{
-    int i = 1;
-
-    if (argc == i)
-        printUsageStatement(false);
-
-    for (; i < argc; ++i) {
-        const char* arg = argv[i];
-
-        if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) {
-            printUsageStatement(true);
-            RELEASE_ASSERT_NOT_REACHED();
-        }
-
-        if (!strcmp(arg, "-w") || !strcmp(arg, "--web")) {
-            m_runWasmTests = true;
-            continue;
-        }
-
-        fprintf(stderr, "Unknown option %s\n", arg);
-        printUsageStatement(false);
-    }
-}
-
-StaticLock crashLock;
-
-#define CHECK_EQ(x, y) do { \
-        auto __x = (x); \
-        auto __y = (y); \
-        if (__x == __y) \
-        break; \
-        crashLock.lock(); \
-        WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, toCString(#x " == " #y, " (" #x " == ", __x, ", " #y " == ", __y, ")").data()); \
-        CRASH(); \
-    } while (false)
-
-#define CHECK(x) CHECK_EQ(x, true)
-
-#if ENABLE(WEBASSEMBLY)
-
-static JSC::VM* vm;
-
-using namespace JSC;
-using namespace Wasm;
-using namespace B3;
-
-template<typename T>
-T cast(EncodedJSValue value)
-{
-    return static_cast<T>(value);
-}
-
-template<>
-double cast(EncodedJSValue value)
-{
-    return bitwise_cast<double>(value);
-}
-
-template<>
-float cast(EncodedJSValue value)
-{
-    return bitwise_cast<float>(static_cast<int>(value));
-}
-
-template<typename T>
-T invoke(MacroAssemblerCodePtr ptr, std::initializer_list<JSValue> args)
-{
-    JSValue firstArgument;
-    // Since vmEntryToJavaScript expects a this value we claim there is one... there isn't.
-    int argCount = 1;
-    JSValue* remainingArguments = nullptr;
-    if (args.size()) {
-        remainingArguments = const_cast<JSValue*>(args.begin());
-        firstArgument = *remainingArguments;
-        remainingArguments++;
-        argCount = args.size();
-    }
-
-    ProtoCallFrame protoCallFrame;
-    protoCallFrame.init(nullptr, nullptr, firstArgument, argCount, remainingArguments);
-
-    return cast<T>(vmEntryToWasm(ptr.executableAddress(), vm, &protoCallFrame));
-}
-
-template<typename T>
-T invoke(const Compilation& code, std::initializer_list<JSValue> args)
-{
-    return invoke<T>(code.code(), args);
-}
-
-inline JSValue box(uint64_t value)
-{
-    return JSValue::decode(value);
-}
-
-inline JSValue boxf(float value)
-{
-    return box(bitwise_cast<uint32_t>(value));
-}
-
-inline JSValue boxd(double value)
-{
-    return box(bitwise_cast<uint64_t>(value));
-}
-
-static void checkPlan(Plan& plan, unsigned expectedNumberOfFunctions)
-{
-    plan.run();
-    if (plan.failed()) {
-        dataLogLn("Module failed to compile with error: ", plan.errorMessage());
-        CRASH();
-    }
-
-    if (plan.internalFunctionCount() != expectedNumberOfFunctions) {
-        dataLogLn("Incorrect number of functions");
-        CRASH();
-    }
-
-    for (unsigned i = 0; i < expectedNumberOfFunctions; ++i) {
-        if (!plan.jsToWasmEntryPointForFunction(i)) {
-            dataLogLn("Function at index, " , i, " failed to compile correctly");
-            CRASH();
-        }
-    }
-
-}
-
-// For now we inline the test files.
-static void runWasmTests()
-{
-    {
-        // Generated from:
-        //    (module
-        //     (func (export "br_table-with-loop") (param $x i32) (result i32)
-        //      (local $i i32)
-        //      (loop
-        //       (block
-        //        (get_local $x)
-        //        (set_local $i (i32.add (get_local $i) (i32.const 1)))
-        //        (set_local $x (i32.sub (get_local $x) (i32.const 1)))
-        //        (br_table 0 1)
-        //        )
-        //       )
-        //      (get_local $i)
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x96, 0x80, 0x80,
-            0x80, 0x00, 0x01, 0x12, 0x62, 0x72, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2d, 0x77, 0x69, 0x74,
-            0x68, 0x2d, 0x6c, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x0a, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x01, 0xa0,
-            0x80, 0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x00, 0x14, 0x00, 0x14, 0x01, 0x10,
-            0x01, 0x40, 0x15, 0x01, 0x14, 0x00, 0x10, 0x01, 0x41, 0x15, 0x00, 0x08, 0x01, 0x00, 0x01, 0x0f,
-            0x0f, 0x14, 0x01, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1) }), 2);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100) }), 101);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(122) }), 123);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func (export "multiple-value") (param i32) (result i32)
-        //      (local i32)
-        //      (set_local 1 (block i32
-        //        (set_local 1 (block i32
-        //          (set_local 1 (block i32
-        //            (set_local 1 (block i32
-        //              (set_local 1 (block i32
-        //                (br_table 3 2 1 0 4 (i32.const 200) (get_local 0))
-        //                (return (i32.add (get_local 1) (i32.const 99)))
-        //                ))
-        //              (return (i32.add (get_local 1) (i32.const 10)))
-        //              ))
-        //            (return (i32.add (get_local 1) (i32.const 11)))
-        //            ))
-        //          (return (i32.add (get_local 1) (i32.const 12)))
-        //          ))
-        //        (return (i32.add (get_local 1) (i32.const 13)))
-        //        ))
-        //      (i32.add (get_local 1) (i32.const 14))
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x92, 0x80, 0x80,
-            0x80, 0x00, 0x01, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x2d, 0x76, 0x61, 0x6c,
-            0x75, 0x65, 0x00, 0x00, 0x0a, 0xd3, 0x80, 0x80, 0x80, 0x00, 0x01, 0xcd, 0x80, 0x80, 0x80, 0x00,
-            0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0xc8, 0x01,
-            0x14, 0x00, 0x08, 0x04, 0x03, 0x02, 0x01, 0x00, 0x04, 0x14, 0x01, 0x10, 0xe3, 0x00, 0x40, 0x09,
-            0x0f, 0x15, 0x01, 0x14, 0x01, 0x10, 0x0a, 0x40, 0x09, 0x0f, 0x15, 0x01, 0x14, 0x01, 0x10, 0x0b,
-            0x40, 0x09, 0x0f, 0x15, 0x01, 0x14, 0x01, 0x10, 0x0c, 0x40, 0x09, 0x0f, 0x15, 0x01, 0x14, 0x01,
-            0x10, 0x0d, 0x40, 0x09, 0x0f, 0x15, 0x01, 0x14, 0x01, 0x10, 0x0e, 0x40, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0) }), 213);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1) }), 212);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2) }), 211);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(3) }), 210);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(3) }), 210);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(4) }), 214);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(5) }), 214);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(-1) }), 214);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(-1000) }), 214);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func (export "singleton") (param i32) (result i32)
-        //      (block
-        //       (block
-        //        (br_table 1 0 (get_local 0))
-        //        (return (i32.const 21))
-        //        )
-        //       (return (i32.const 20))
-        //       )
-        //      (i32.const 22)
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x8d, 0x80, 0x80,
-            0x80, 0x00, 0x01, 0x09, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x74, 0x6f, 0x6e, 0x00, 0x00, 0x0a,
-            0x9c, 0x80, 0x80, 0x80, 0x00, 0x01, 0x96, 0x80, 0x80, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
-            0x14, 0x00, 0x08, 0x01, 0x01, 0x00, 0x10, 0x15, 0x09, 0x0f, 0x10, 0x14, 0x09, 0x0f, 0x10, 0x16,
-            0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0) }), 22);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1) }), 20);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(11) }), 20);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(-100) }), 20);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func (export "if-then-both-fallthrough") (param $x i32) (param $y i32) (result i32)
-        //      (block $block i32
-        //       (if i32 (i32.eq (get_local $x) (i32.const 0))
-        //        (then (i32.const 1))
-        //        (else
-        //         (i32.const 2)
-        //         (br $block))
-        //        )
-        //       )
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x9c, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x18, 0x69, 0x66, 0x2d, 0x74, 0x68, 0x65, 0x6e, 0x2d, 0x62, 0x6f, 0x74,
-            0x68, 0x2d, 0x66, 0x61, 0x6c, 0x6c, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x00, 0x00, 0x0a,
-            0x9a, 0x80, 0x80, 0x80, 0x00, 0x01, 0x94, 0x80, 0x80, 0x80, 0x00, 0x00, 0x01, 0x01, 0x14, 0x00,
-            0x10, 0x00, 0x4d, 0x03, 0x01, 0x10, 0x01, 0x04, 0x10, 0x02, 0x06, 0x01, 0x0f, 0x0f, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(32) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(32) }), 2);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func (export "if-then-both-fallthrough") (param $x i32) (param $y i32) (result i32)
-        //      (if i32 (i32.eq (get_local $x) (i32.const 0))
-        //       (then (i32.const 1))
-        //       (else (i32.const 2))
-        //       )
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x9c, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x18, 0x69, 0x66, 0x2d, 0x74, 0x68, 0x65, 0x6e, 0x2d, 0x62, 0x6f, 0x74,
-            0x68, 0x2d, 0x66, 0x61, 0x6c, 0x6c, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x00, 0x00, 0x0a,
-            0x95, 0x80, 0x80, 0x80, 0x00, 0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00, 0x10, 0x00,
-            0x4d, 0x03, 0x01, 0x10, 0x01, 0x04, 0x10, 0x02, 0x0f, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(32) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(32) }), 2);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func (export "dumb-less-than") (param $x i32) (param $y i32) (result i32)
-        //      (loop $loop i32
-        //       (i32.eq (get_local $x) (get_local $y))
-        //       (if i32
-        //        (then (return (i32.const 0)))
-        //        (else
-        //         (get_local $x)
-        //         (set_local $x (i32.sub (get_local $x) (i32.const 1)))
-        //         (i32.const 0)
-        //         (i32.ne)
-        //         (br_if $loop)
-        //         (i32.const 1)
-        //         )
-        //        )
-        //       )
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x92, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x0e, 0x64, 0x75, 0x6d, 0x62, 0x2d, 0x6c, 0x65, 0x73, 0x73, 0x2d, 0x74,
-            0x68, 0x61, 0x6e, 0x00, 0x00, 0x0a, 0xa7, 0x80, 0x80, 0x80, 0x00, 0x01, 0xa1, 0x80, 0x80, 0x80,
-            0x00, 0x00, 0x02, 0x01, 0x14, 0x00, 0x14, 0x01, 0x4d, 0x03, 0x01, 0x10, 0x00, 0x09, 0x04, 0x14,
-            0x00, 0x14, 0x00, 0x10, 0x01, 0x41, 0x15, 0x00, 0x10, 0x00, 0x4e, 0x07, 0x01, 0x10, 0x01, 0x0f,
-            0x0f, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(1) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(1) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(2) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(2) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(1) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(6) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(6) }), 0);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func $f32-sub (export "f32-sub") (param f32) (param f32) (result f32) (return (f32.sub (get_local 0) (get_local 1))))
-        //     (func (export "indirect-f32-sub") (param f32) (param f32) (result f32) (return (call $f32-sub (get_local 0) (get_local 1))))
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x03, 0x03, 0x01, 0x03, 0x03, 0x83, 0x80, 0x80, 0x80, 0x00, 0x02, 0x00, 0x00, 0x07, 0x9e,
-            0x80, 0x80, 0x80, 0x00, 0x02, 0x07, 0x66, 0x33, 0x32, 0x2d, 0x73, 0x75, 0x62, 0x00, 0x00, 0x10,
-            0x69, 0x6e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x2d, 0x66, 0x33, 0x32, 0x2d, 0x73, 0x75, 0x62,
-            0x00, 0x01, 0x0a, 0x9c, 0x80, 0x80, 0x80, 0x00, 0x02, 0x88, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14,
-            0x00, 0x14, 0x01, 0x76, 0x09, 0x0f, 0x89, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00, 0x14, 0x01,
-            0x16, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 2);
-
-        // Test this doesn't crash.
-        CHECK(isIdentical(invoke<float>(*plan.jsToWasmEntryPointForFunction(1), { boxf(0.0), boxf(1.5) }), -1.5f));
-        CHECK(isIdentical(invoke<float>(*plan.jsToWasmEntryPointForFunction(1), { boxf(100.1234), boxf(12.5) }), 87.6234f));
-        CHECK(isIdentical(invoke<float>(*plan.jsToWasmEntryPointForFunction(0), { boxf(0.0), boxf(1.5) }), -1.5f));
-        CHECK(isIdentical(invoke<float>(*plan.jsToWasmEntryPointForFunction(0), { boxf(100.1234), boxf(12.5) }), 87.6234f));
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func $f32-add (export "f32-add") (param f32) (param f32) (result f32) (return (f32.add (get_local 0) (get_local 1))))
-        //     (func (export "indirect-f32-add") (param f32) (param f32) (result f32) (return (call $f32-add (get_local 0) (get_local 1))))
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x03, 0x03, 0x01, 0x03, 0x03, 0x83, 0x80, 0x80, 0x80, 0x00, 0x02, 0x00, 0x00, 0x07, 0x9e,
-            0x80, 0x80, 0x80, 0x00, 0x02, 0x07, 0x66, 0x33, 0x32, 0x2d, 0x61, 0x64, 0x64, 0x00, 0x00, 0x10,
-            0x69, 0x6e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x2d, 0x66, 0x33, 0x32, 0x2d, 0x61, 0x64, 0x64,
-            0x00, 0x01, 0x0a, 0x9c, 0x80, 0x80, 0x80, 0x00, 0x02, 0x88, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14,
-            0x00, 0x14, 0x01, 0x75, 0x09, 0x0f, 0x89, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00, 0x14, 0x01,
-            0x16, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 2);
-
-        // Test this doesn't crash.
-        CHECK(isIdentical(invoke<float>(*plan.jsToWasmEntryPointForFunction(1), { boxf(0.0), boxf(1.5) }), 1.5f));
-        CHECK(isIdentical(invoke<float>(*plan.jsToWasmEntryPointForFunction(1), { boxf(100.1234), boxf(12.5) }), 112.6234f));
-        CHECK(isIdentical(invoke<float>(*plan.jsToWasmEntryPointForFunction(0), { boxf(0.0), boxf(1.5) }), 1.5f));
-        CHECK(isIdentical(invoke<float>(*plan.jsToWasmEntryPointForFunction(0), { boxf(100.1234), boxf(12.5) }), 112.6234f));
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func $sum12 (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32) (return (i32.add (get_local 0) (i32.add (get_local 1) (i32.add (get_local 2) (i32.add (get_local 3) (i32.add (get_local 4) (i32.add (get_local 5) (i32.add (get_local 6) (i32.add (get_local 7) (i32.add (get_local 8) (i32.add (get_local 9) (i32.add (get_local 10) (get_local 11))))))))))))))
-        //     (func (export "mult12") (param i32) (result i32) (return (call $sum12 (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0))))
-        //     )
-
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x96, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40,
-            0x0c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40,
-            0x01, 0x01, 0x01, 0x01, 0x03, 0x83, 0x80, 0x80, 0x80, 0x00, 0x02, 0x00, 0x01, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x00, 0x01, 0x07, 0x8a, 0x80, 0x80, 0x80, 0x00, 0x01, 0x06, 0x6d, 0x75,
-            0x6c, 0x74, 0x31, 0x32, 0x00, 0x01, 0x0a, 0xce, 0x80, 0x80, 0x80, 0x00, 0x02, 0xa6, 0x80, 0x80,
-            0x80, 0x00, 0x00, 0x14, 0x00, 0x14, 0x01, 0x14, 0x02, 0x14, 0x03, 0x14, 0x04, 0x14, 0x05, 0x14,
-            0x06, 0x14, 0x07, 0x14, 0x08, 0x14, 0x09, 0x14, 0x0a, 0x14, 0x0b, 0x40, 0x40, 0x40, 0x40, 0x40,
-            0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x09, 0x0f, 0x9d, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00,
-            0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00,
-            0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x16, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 2);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(1), { box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(1), { box(100) }), 1200);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(1), { box(1) }), 12);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(2), box(3), box(4), box(5), box(6), box(7), box(8), box(9), box(10), box(11), box(12) }), 78);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(2), box(3), box(4), box(5), box(6), box(7), box(8), box(9), box(10), box(11), box(100) }), 166);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func $fac (export "fac") (param i64) (result i64)
-        //      (if (i64.eqz (get_local 0))
-        //       (return (i64.const 1))
-        //       )
-        //      (return (i64.mul (get_local 0) (call $fac (i64.sub (get_local 0) (i64.const 1)))))
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x01, 0x02, 0x01, 0x02, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80, 0x80,
-            0x80, 0x00, 0x01, 0x00, 0x01, 0x07, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x03, 0x66, 0x61, 0x63,
-            0x00, 0x00, 0x0a, 0x9e, 0x80, 0x80, 0x80, 0x00, 0x01, 0x98, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14,
-            0x00, 0x11, 0x00, 0x68, 0x03, 0x00, 0x11, 0x01, 0x09, 0x0f, 0x14, 0x00, 0x14, 0x00, 0x11, 0x01,
-            0x5c, 0x16, 0x00, 0x5d, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2) }), 2);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(4) }), 24);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func (export "double") (param i64) (result i64) (return (call 1 (get_local 0) (get_local 0))))
-        //     (func $sum (param i64) (param i64) (result i64) (return (i64.add (get_local 0) (get_local 1))))
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x8c, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40,
-            0x01, 0x02, 0x01, 0x02, 0x40, 0x02, 0x02, 0x02, 0x01, 0x02, 0x03, 0x83, 0x80, 0x80, 0x80, 0x00,
-            0x02, 0x00, 0x01, 0x05, 0x83, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x01, 0x07, 0x8a, 0x80, 0x80,
-            0x80, 0x00, 0x01, 0x06, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x00, 0x00, 0x0a, 0x9c, 0x80, 0x80,
-            0x80, 0x00, 0x02, 0x89, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00, 0x14, 0x00, 0x16, 0x01, 0x09,
-            0x0f, 0x88, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00, 0x14, 0x01, 0x5b, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 2);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(1), { box(0), box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(1), { box(100), box(0) }), 100);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(1), { box(1), box(15) }), 16);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100) }), 200);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1) }), 2);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func $id (param $value i32) (result i32) (return (get_local $value)))
-        //     (func (export "id-call") (param $value i32) (result i32) (return (call $id (get_local $value))))
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x01, 0x01, 0x01, 0x01, 0x03, 0x83, 0x80, 0x80, 0x80, 0x00, 0x02, 0x00, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8b, 0x80, 0x80, 0x80, 0x00, 0x01, 0x07, 0x69, 0x64,
-            0x2d, 0x63, 0x61, 0x6c, 0x6c, 0x00, 0x01, 0x0a, 0x97, 0x80, 0x80, 0x80, 0x00, 0x02, 0x85, 0x80,
-            0x80, 0x80, 0x00, 0x00, 0x14, 0x00, 0x09, 0x0f, 0x87, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00,
-            0x16, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 2);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(1), { box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(1), { box(100) }), 100);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(1), { box(1) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100) }), 100);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1) }), 1);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func (export "i64") (param $i i64) (param $ptr i32) (result i64)
-        //      (i64.store (get_local $ptr) (get_local $i))
-        //      (return (i64.load (get_local $ptr)))
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x02, 0x01, 0x01, 0x02, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x03, 0x69, 0x36,
-            0x34, 0x00, 0x00, 0x0a, 0x95, 0x80, 0x80, 0x80, 0x00, 0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00,
-            0x14, 0x01, 0x14, 0x00, 0x34, 0x03, 0x00, 0x14, 0x01, 0x2b, 0x03, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(10) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(2) }), 100);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(100) }), 1);
-    }
-
-    {
-        // Generated from:
-        // (module
-        //  (memory 1)
-        //  (func (export "i32_load8_s") (param $i i32) (param $ptr i32) (result i32)
-        //   (i32.store (get_local $ptr) (get_local $i))
-        //   (return (i32.load (get_local $ptr)))
-        //   )
-        //  )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x69, 0x33,
-            0x32, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x38, 0x5f, 0x73, 0x00, 0x00, 0x0a, 0x95, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x01, 0x14, 0x00, 0x33, 0x02, 0x00, 0x14,
-            0x01, 0x2a, 0x02, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(10) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(2) }), 100);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(100) }), 1);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func (export "write_array") (param $x i32) (param $p i32) (param $length i32) (local $i i32)
-        //      (set_local $i (i32.const 0))
-        //      (block
-        //       (loop
-        //        (br_if 1 (i32.ge_u (get_local $i) (get_local $length)))
-        //        (i32.store (i32.add (get_local $p) (i32.mul (get_local $i) (i32.const 4))) (get_local $x))
-        //        (set_local $i (i32.add (i32.const 1) (get_local $i)))
-        //        (br 0)
-        //        )
-        //       )
-        //      (return)
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x03, 0x01, 0x01, 0x01, 0x00, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x77, 0x72,
-            0x69, 0x74, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x0a, 0xb2, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0xac, 0x80, 0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x15, 0x03, 0x01, 0x00,
-            0x02, 0x00, 0x14, 0x03, 0x14, 0x02, 0x56, 0x07, 0x01, 0x14, 0x01, 0x14, 0x03, 0x10, 0x04, 0x42,
-            0x40, 0x14, 0x00, 0x33, 0x02, 0x00, 0x10, 0x01, 0x14, 0x03, 0x40, 0x15, 0x03, 0x06, 0x00, 0x0f,
-            0x0f, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-        ASSERT(plan.memory()->size());
-
-        // Test this doesn't crash.
-        unsigned length = 5;
-        unsigned offset = sizeof(uint32_t);
-        uint32_t* memory = static_cast<uint32_t*>(plan.memory()->memory());
-        invoke<void>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(offset), box(length) });
-        offset /= sizeof(uint32_t);
-        CHECK_EQ(memory[offset - 1], 0u);
-        CHECK_EQ(memory[offset + length], 0u);
-        for (unsigned i = 0; i < length; ++i)
-            CHECK_EQ(memory[i + offset], 100u);
-
-        length = 10;
-        offset = 5 * sizeof(uint32_t);
-        invoke<void>(*plan.jsToWasmEntryPointForFunction(0), { box(5), box(offset), box(length) });
-        offset /= sizeof(uint32_t);
-        CHECK_EQ(memory[offset - 1], 100u);
-        CHECK_EQ(memory[offset + length], 0u);
-        for (unsigned i = 0; i < length; ++i)
-            CHECK_EQ(memory[i + offset], 5u);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func (export "write_array") (param $x i32) (param $p i32) (param $length i32) (local $i i32)
-        //      (set_local $i (i32.const 0))
-        //      (block
-        //       (loop
-        //        (br_if 1 (i32.ge_u (get_local $i) (get_local $length)))
-        //        (i32.store8 (i32.add (get_local $p) (get_local $i)) (get_local $x))
-        //        (set_local $i (i32.add (i32.const 1) (get_local $i)))
-        //        (br 0)
-        //        )
-        //       )
-        //      (return)
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x03, 0x01, 0x01, 0x01, 0x00, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x77, 0x72,
-            0x69, 0x74, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x0a, 0xaf, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0xa9, 0x80, 0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x15, 0x03, 0x01, 0x00,
-            0x02, 0x00, 0x14, 0x03, 0x14, 0x02, 0x56, 0x07, 0x01, 0x14, 0x01, 0x14, 0x03, 0x40, 0x14, 0x00,
-            0x2e, 0x00, 0x00, 0x10, 0x01, 0x14, 0x03, 0x40, 0x15, 0x03, 0x06, 0x00, 0x0f, 0x0f, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-        ASSERT(plan.memory()->size());
-
-        // Test this doesn't crash.
-        unsigned length = 5;
-        unsigned offset = 1;
-        uint8_t* memory = static_cast<uint8_t*>(plan.memory()->memory());
-        invoke<void>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(offset), box(length) });
-        CHECK_EQ(memory[offset - 1], 0u);
-        CHECK_EQ(memory[offset + length], 0u);
-        for (unsigned i = 0; i < length; ++i)
-            CHECK_EQ(memory[i + offset], 100u);
-
-        length = 10;
-        offset = 5;
-        invoke<void>(*plan.jsToWasmEntryPointForFunction(0), { box(5), box(offset), box(length) });
-        CHECK_EQ(memory[offset - 1], 100u);
-        CHECK_EQ(memory[offset + length], 0u);
-        for (unsigned i = 0; i < length; ++i)
-            CHECK_EQ(memory[i + offset], 5u);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func (export "i32_load8_s") (param $i i32) (param $ptr i32) (result i32)
-        //      (i32.store8 (get_local $ptr) (get_local $i))
-        //      (return (i32.load8_s (get_local $ptr)))
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x69, 0x33,
-            0x32, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x38, 0x5f, 0x73, 0x00, 0x00, 0x0a, 0x95, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x01, 0x14, 0x00, 0x2e, 0x00, 0x00, 0x14,
-            0x01, 0x20, 0x00, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-        ASSERT(plan.memory()->size());
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(10) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(2) }), 100);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(100) }), 1);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func (export "i32_load8_s") (param $i i32) (result i32)
-        //      (i32.store8 (i32.const 8) (get_local $i))
-        //      (return (i32.load8_s (i32.const 8)))
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80, 0x80,
-            0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x69, 0x33, 0x32,
-            0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x38, 0x5f, 0x73, 0x00, 0x00, 0x0a, 0x95, 0x80, 0x80, 0x80, 0x00,
-            0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00, 0x10, 0x08, 0x14, 0x00, 0x2e, 0x00, 0x00, 0x10, 0x08,
-            0x20, 0x00, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100) }), 100);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1) }), 1);
-    }
-
-    {
-        // Generated from:
-        // (module
-        //  (memory 1)
-        //  (func (export "i32_load8_s") (param $i i32) (param $ptr i32) (result i32)
-        //   (i32.store (get_local $ptr) (get_local $i))
-        //   (return (i32.load (get_local $ptr)))
-        //   )
-        //  )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x02, 0x01, 0x01, 0x02, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x69, 0x33,
-            0x32, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x38, 0x5f, 0x73, 0x00, 0x00, 0x0a, 0x95, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x01, 0x14, 0x00, 0x34, 0x03, 0x00, 0x14,
-            0x01, 0x2b, 0x03, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(10) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(2) }), 100);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(100) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(-12), box(plan.memory()->size() - sizeof(uint64_t)) }), -12);
-    }
-
-    {
-        // Generated from:
-        // (module
-        //  (memory 1)
-        //  (func (export "i32_load8_s") (param $i i32) (param $ptr i32) (result i32)
-        //   (i32.store (get_local $ptr) (get_local $i))
-        //   (return (i32.load (get_local $ptr)))
-        //   )
-        //  )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x69, 0x33,
-            0x32, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x38, 0x5f, 0x73, 0x00, 0x00, 0x0a, 0x95, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x01, 0x14, 0x00, 0x33, 0x02, 0x00, 0x14,
-            0x01, 0x2a, 0x02, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(10) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(2) }), 100);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(100) }), 1);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func (export "write_array") (param $x i32) (param $p i32) (param $length i32) (local $i i32)
-        //      (set_local $i (i32.const 0))
-        //      (block
-        //       (loop
-        //        (br_if 1 (i32.ge_u (get_local $i) (get_local $length)))
-        //        (i32.store (i32.add (get_local $p) (i32.mul (get_local $i) (i32.const 4))) (get_local $x))
-        //        (set_local $i (i32.add (i32.const 1) (get_local $i)))
-        //        (br 0)
-        //        )
-        //       )
-        //      (return)
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x03, 0x01, 0x01, 0x01, 0x00, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x77, 0x72,
-            0x69, 0x74, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x0a, 0xb2, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0xac, 0x80, 0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x15, 0x03, 0x01, 0x00,
-            0x02, 0x00, 0x14, 0x03, 0x14, 0x02, 0x56, 0x07, 0x01, 0x14, 0x01, 0x14, 0x03, 0x10, 0x04, 0x42,
-            0x40, 0x14, 0x00, 0x33, 0x02, 0x00, 0x10, 0x01, 0x14, 0x03, 0x40, 0x15, 0x03, 0x06, 0x00, 0x0f,
-            0x0f, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-        ASSERT(plan.memory()->size());
-
-        // Test this doesn't crash.
-        unsigned length = 5;
-        unsigned offset = sizeof(uint32_t);
-        uint32_t* memory = static_cast<uint32_t*>(plan.memory()->memory());
-        invoke<void>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(offset), box(length) });
-        offset /= sizeof(uint32_t);
-        CHECK_EQ(memory[offset - 1], 0u);
-        CHECK_EQ(memory[offset + length], 0u);
-        for (unsigned i = 0; i < length; ++i)
-            CHECK_EQ(memory[i + offset], 100u);
-
-        length = 10;
-        offset = 5 * sizeof(uint32_t);
-        invoke<void>(*plan.jsToWasmEntryPointForFunction(0), { box(5), box(offset), box(length) });
-        offset /= sizeof(uint32_t);
-        CHECK_EQ(memory[offset - 1], 100u);
-        CHECK_EQ(memory[offset + length], 0u);
-        for (unsigned i = 0; i < length; ++i)
-            CHECK_EQ(memory[i + offset], 5u);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func (export "write_array") (param $x i32) (param $p i32) (param $length i32) (local $i i32)
-        //      (set_local $i (i32.const 0))
-        //      (block
-        //       (loop
-        //        (br_if 1 (i32.ge_u (get_local $i) (get_local $length)))
-        //        (i32.store8 (i32.add (get_local $p) (get_local $i)) (get_local $x))
-        //        (set_local $i (i32.add (i32.const 1) (get_local $i)))
-        //        (br 0)
-        //        )
-        //       )
-        //      (return)
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x03, 0x01, 0x01, 0x01, 0x00, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x77, 0x72,
-            0x69, 0x74, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x0a, 0xaf, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0xa9, 0x80, 0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x15, 0x03, 0x01, 0x00,
-            0x02, 0x00, 0x14, 0x03, 0x14, 0x02, 0x56, 0x07, 0x01, 0x14, 0x01, 0x14, 0x03, 0x40, 0x14, 0x00,
-            0x2e, 0x00, 0x00, 0x10, 0x01, 0x14, 0x03, 0x40, 0x15, 0x03, 0x06, 0x00, 0x0f, 0x0f, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-        ASSERT(plan.memory()->size());
-
-        // Test this doesn't crash.
-        unsigned length = 5;
-        unsigned offset = 1;
-        uint8_t* memory = static_cast<uint8_t*>(plan.memory()->memory());
-        invoke<void>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(offset), box(length) });
-        CHECK_EQ(memory[offset - 1], 0u);
-        CHECK_EQ(memory[offset + length], 0u);
-        for (unsigned i = 0; i < length; ++i)
-            CHECK_EQ(memory[i + offset], 100u);
-
-        length = 10;
-        offset = 5;
-        invoke<void>(*plan.jsToWasmEntryPointForFunction(0), { box(5), box(offset), box(length) });
-        CHECK_EQ(memory[offset - 1], 100u);
-        CHECK_EQ(memory[offset + length], 0u);
-        for (unsigned i = 0; i < length; ++i)
-            CHECK_EQ(memory[i + offset], 5u);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func (export "i32_load8_s") (param $i i32) (param $ptr i32) (result i32)
-        //      (i32.store8 (get_local $ptr) (get_local $i))
-        //      (return (i32.load8_s (get_local $ptr)))
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x69, 0x33,
-            0x32, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x38, 0x5f, 0x73, 0x00, 0x00, 0x0a, 0x95, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x01, 0x14, 0x00, 0x2e, 0x00, 0x00, 0x14,
-            0x01, 0x20, 0x00, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-        ASSERT(plan.memory()->size());
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(10) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(2) }), 100);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(100) }), 1);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (memory 1)
-        //     (func (export "i32_load8_s") (param $i i32) (result i32)
-        //      (i32.store8 (i32.const 8) (get_local $i))
-        //      (return (i32.load8_s (i32.const 8)))
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80, 0x80,
-            0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x69, 0x33, 0x32,
-            0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x38, 0x5f, 0x73, 0x00, 0x00, 0x0a, 0x95, 0x80, 0x80, 0x80, 0x00,
-            0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00, 0x10, 0x08, 0x14, 0x00, 0x2e, 0x00, 0x00, 0x10, 0x08,
-            0x20, 0x00, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100) }), 100);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1) }), 1);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func "dumb-eq" (param $x i32) (param $y i32) (result i32)
-        //      (if (i32.eq (get_local $x) (get_local $y))
-        //       (then (br 0))
-        //       (else (return (i32.const 1))))
-        //      (return (i32.const 0))
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x8b, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x07, 0x64, 0x75, 0x6d, 0x62, 0x2d, 0x65, 0x71, 0x00, 0x00, 0x0a, 0x99,
-            0x80, 0x80, 0x80, 0x00, 0x01, 0x93, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00, 0x14, 0x01, 0x4d,
-            0x03, 0x00, 0x06, 0x00, 0x04, 0x10, 0x01, 0x09, 0x0f, 0x10, 0x00, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(1) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(0) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(1) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(2) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(2) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(1) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(6) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(6) }), 1);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func (export "dumb-less-than") (param $x i32) (param $y i32) (result i32)
-        //      (loop
-        //       (if (i32.eq (get_local $x) (get_local $y))
-        //        (then (return (i32.const 0)))
-        //        (else (if (i32.eq (get_local $x) (i32.const 0))
-        //               (return (i32.const 1)))))
-        //       (set_local $x (i32.sub (get_local $x) (i32.const 1)))
-        //       (br 0)
-        //       )
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x92, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x0e, 0x64, 0x75, 0x6d, 0x62, 0x2d, 0x6c, 0x65, 0x73, 0x73, 0x2d, 0x74,
-            0x68, 0x61, 0x6e, 0x00, 0x00, 0x0a, 0xac, 0x80, 0x80, 0x80, 0x00, 0x01, 0xa6, 0x80, 0x80, 0x80,
-            0x00, 0x00, 0x02, 0x00, 0x14, 0x00, 0x14, 0x01, 0x4d, 0x03, 0x00, 0x10, 0x00, 0x09, 0x04, 0x14,
-            0x00, 0x10, 0x00, 0x4d, 0x03, 0x00, 0x10, 0x01, 0x09, 0x0f, 0x0f, 0x14, 0x00, 0x10, 0x01, 0x41,
-            0x15, 0x00, 0x06, 0x00, 0x0f, 0x00, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(1) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(1) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(2) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(2) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(1) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(6) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(6) }), 0);
-    }
-
-
-    {
-        // Generated from: (module (func (export "return-i32") (result i32) (return (i32.const 5))) )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x00, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x8e, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x2d, 0x69, 0x33, 0x32, 0x00, 0x00, 0x0a,
-            0x8b, 0x80, 0x80, 0x80, 0x00, 0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x00, 0x10, 0x05, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { }), 5);
-    }
-
-
-    {
-        // Generated from: (module (func (export "return-i32") (result i32) (return (i32.add (i32.const 5) (i32.const 6)))) )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x00, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x8e, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x2d, 0x69, 0x33, 0x32, 0x00, 0x00, 0x0a,
-            0x8e, 0x80, 0x80, 0x80, 0x00, 0x01, 0x88, 0x80, 0x80, 0x80, 0x00, 0x00, 0x10, 0x05, 0x10, 0x06,
-            0x40, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { }), 11);
-    }
-
-    {
-        // Generated from: (module (func (export "return-i32") (result i32) (return (i32.add (i32.add (i32.const 5) (i32.const 3)) (i32.const 3)))) )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x00, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x8e, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x2d, 0x69, 0x33, 0x32, 0x00, 0x00, 0x0a,
-            0x91, 0x80, 0x80, 0x80, 0x00, 0x01, 0x8b, 0x80, 0x80, 0x80, 0x00, 0x00, 0x10, 0x05, 0x10, 0x03,
-            0x40, 0x10, 0x03, 0x40, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { }), 11);
-    }
-
-    {
-        // Generated from: (module (func (export "return-i32") (result i32) (block (return (i32.add (i32.add (i32.const 5) (i32.const 3)) (i32.const 3)))) (unreachable)) )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x00, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x8e, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x2d, 0x69, 0x33, 0x32, 0x00, 0x00, 0x0a,
-            0x95, 0x80, 0x80, 0x80, 0x00, 0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00, 0x01, 0x00, 0x10, 0x05,
-            0x10, 0x03, 0x40, 0x10, 0x03, 0x40, 0x09, 0x0f, 0x00, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { }), 11);
-    }
-
-    {
-        // Generated from: (module (func (export "add") (param $x i32) (param $y i32) (result i32) (return (i32.add (get_local $x) (get_local $y)))) )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x87, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00, 0x0a, 0x8e, 0x80, 0x80, 0x80, 0x00,
-            0x01, 0x88, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00, 0x14, 0x01, 0x40, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(1) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(1) }), 101);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(-1), box(1)}), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(std::numeric_limits<int>::max()), box(1) }), std::numeric_limits<int>::min());
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func (export "locals") (param $x i32) (result i32) (local $num i32)
-        //      (set_local $num (get_local $x))
-        //      (return (get_local $num))
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x8a, 0x80, 0x80,
-            0x80, 0x00, 0x01, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x00, 0x00, 0x0a, 0x91, 0x80, 0x80,
-            0x80, 0x00, 0x01, 0x8b, 0x80, 0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x14, 0x00, 0x15, 0x01, 0x14,
-            0x01, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(10) }), 10);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func (export "sum") (param $x i32) (result i32) (local $y i32)
-        //      (set_local $y (i32.const 0))
-        //      (loop
-        //       (block
-        //        (br_if 0 (i32.eq (get_local $x) (i32.const 0)))
-        //        (set_local $y (i32.add (get_local $x) (get_local $y)))
-        //        (set_local $x (i32.sub (get_local $x) (i32.const 1)))
-        //        (br 1)
-        //        )
-        //       )
-        //      (return (get_local $y))
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x87, 0x80, 0x80,
-            0x80, 0x00, 0x01, 0x03, 0x73, 0x75, 0x6d, 0x00, 0x00, 0x0a, 0xae, 0x80, 0x80, 0x80, 0x00, 0x01,
-            0xa8, 0x80, 0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x15, 0x01, 0x02, 0x00, 0x01, 0x00,
-            0x14, 0x00, 0x10, 0x00, 0x4d, 0x07, 0x00, 0x14, 0x00, 0x14, 0x01, 0x40, 0x15, 0x01, 0x14, 0x00,
-            0x10, 0x01, 0x41, 0x15, 0x00, 0x06, 0x01, 0x0f, 0x0f, 0x14, 0x01, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2)}), 3);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100) }), 5050);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func (export "dumb-mult") (param $x i32) (param $y i32) (result i32) (local $total i32) (local $i i32) (local $j i32)
-        //      (set_local $total (i32.const 0))
-        //      (set_local $i (i32.const 0))
-        //      (block (loop
-        //              (br_if 1 (i32.eq (get_local $i) (get_local $x)))
-        //              (set_local $j (i32.const 0))
-        //              (set_local $i (i32.add (get_local $i) (i32.const 1)))
-        //              (loop
-        //               (br_if 1 (i32.eq (get_local $j) (get_local $y)))
-        //               (set_local $total (i32.add (get_local $total) (i32.const 1)))
-        //               (set_local $j (i32.add (get_local $j) (i32.const 1)))
-        //               (br 0)
-        //               )
-        //              ))
-        //      (return (get_local $total))
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x8d, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x09, 0x64, 0x75, 0x6d, 0x62, 0x2d, 0x6d, 0x75, 0x6c, 0x74, 0x00, 0x00,
-            0x0a, 0xc7, 0x80, 0x80, 0x80, 0x00, 0x01, 0xc1, 0x80, 0x80, 0x80, 0x00, 0x01, 0x03, 0x01, 0x10,
-            0x00, 0x15, 0x02, 0x10, 0x00, 0x15, 0x03, 0x01, 0x00, 0x02, 0x00, 0x14, 0x03, 0x14, 0x00, 0x4d,
-            0x07, 0x01, 0x10, 0x00, 0x15, 0x04, 0x14, 0x03, 0x10, 0x01, 0x40, 0x15, 0x03, 0x02, 0x00, 0x14,
-            0x04, 0x14, 0x01, 0x4d, 0x07, 0x01, 0x14, 0x02, 0x10, 0x01, 0x40, 0x15, 0x02, 0x14, 0x04, 0x10,
-            0x01, 0x40, 0x15, 0x04, 0x06, 0x00, 0x0f, 0x0f, 0x0f, 0x14, 0x02, 0x09, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(1) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(1) }), 2);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(2) }), 2);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(2) }), 4);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(6) }), 12);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(6) }), 600);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(100) }), 10000);
-    }
-
-    {
-        // Generated from:
-        //    (module
-        //     (func "dumb-less-than" (param $x i32) (param $y i32) (result i32)
-        //      (loop
-        //       (block
-        //        (block
-        //         (br_if 0 (i32.eq (get_local $x) (get_local $y)))
-        //         (br 1)
-        //         )
-        //        (return (i32.const 0))
-        //        )
-        //       (block
-        //        (block
-        //         (br_if 0 (i32.eq (get_local $x) (i32.const 0)))
-        //         (br 1)
-        //         )
-        //        (return (i32.const 1))
-        //        )
-        //       (set_local $x (i32.sub (get_local $x) (i32.const 1)))
-        //       (br 0)
-        //       )
-        //       (unreachable)
-        //      )
-        //     )
-        Vector<uint8_t> vector = {
-            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
-            0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x92, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x0e, 0x64, 0x75, 0x6d, 0x62, 0x2d, 0x6c, 0x65, 0x73, 0x73, 0x2d, 0x74,
-            0x68, 0x61, 0x6e, 0x00, 0x00, 0x0a, 0xb9, 0x80, 0x80, 0x80, 0x00, 0x01, 0xb3, 0x80, 0x80, 0x80,
-            0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x14, 0x00, 0x14, 0x01, 0x4d, 0x07, 0x00, 0x06,
-            0x01, 0x0f, 0x10, 0x00, 0x09, 0x0f, 0x01, 0x00, 0x01, 0x00, 0x14, 0x00, 0x10, 0x00, 0x4d, 0x07,
-            0x00, 0x06, 0x01, 0x0f, 0x10, 0x01, 0x09, 0x0f, 0x14, 0x00, 0x10, 0x01, 0x41, 0x15, 0x00, 0x06,
-            0x00, 0x0f, 0x00, 0x0f
-        };
-
-        Plan plan(vm, vector);
-        checkPlan(plan, 1);
-
-        // Test this doesn't crash.
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(0), box(1) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(0) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(1) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(2) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(2) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(1), box(1) }), 0);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(2), box(6) }), 1);
-        CHECK_EQ(invoke<int>(*plan.jsToWasmEntryPointForFunction(0), { box(100), box(6) }), 0);
-    }
-
-}
-
-#endif // ENABLE(WEBASSEMBLY)
-
-int main(int argc, char** argv)
-{
-    CommandLine options(argc, argv);
-
-    if (options.m_runWasmTests) {
-#if ENABLE(WEBASSEMBLY)
-        JSC::initializeThreading();
-        vm = &JSC::VM::create(JSC::LargeHeap).leakRef();
-        runWasmTests();
-#else
-        dataLogLn("Wasm is not enabled!");
-        return EXIT_FAILURE;
-#endif // ENABLE(WEBASSEMBLY)
-    }
-
-    return EXIT_SUCCESS;
-}
-
index 9f39cd7..820e588 100644 (file)
@@ -130,7 +130,7 @@ public:
 
     static constexpr ExpressionType emptyExpression = nullptr;
 
-    B3IRGenerator(Memory*, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&);
+    B3IRGenerator(MemoryInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&);
 
     bool WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
     bool WARN_UNUSED_RETURN addLocal(Type, uint32_t);
@@ -179,7 +179,6 @@ private:
     void unifyValuesWithBlock(const ExpressionList& resultStack, ResultList& stack);
     Value* zeroForType(Type);
 
-    Memory* m_memory;
     Procedure& m_proc;
     BasicBlock* m_currentBlock;
     Vector<Variable*> m_locals;
@@ -189,9 +188,8 @@ private:
     Value* m_zeroValues[numTypes];
 };
 
-B3IRGenerator::B3IRGenerator(Memory* memory, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls)
-    : m_memory(memory)
-    , m_proc(procedure)
+B3IRGenerator::B3IRGenerator(MemoryInformation& memory, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls)
+    : m_proc(procedure)
     , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls)
 {
     m_currentBlock = m_proc.addBlock();
@@ -210,12 +208,12 @@ B3IRGenerator::B3IRGenerator(Memory* memory, Procedure& procedure, WasmInternalF
         }
     }
 
-    if (m_memory) {
-        m_memoryBaseGPR = m_memory->pinnedRegisters().baseMemoryPointer;
+    if (!!memory) {
+        m_memoryBaseGPR = memory.pinnedRegisters().baseMemoryPointer;
         m_proc.pinRegister(m_memoryBaseGPR);
-        ASSERT(!m_memory->pinnedRegisters().sizeRegisters[0].sizeOffset);
-        m_memorySizeGPR = m_memory->pinnedRegisters().sizeRegisters[0].sizeRegister;
-        for (const PinnedSizeRegisterInfo& info : m_memory->pinnedRegisters().sizeRegisters)
+        ASSERT(!memory.pinnedRegisters().sizeRegisters[0].sizeOffset);
+        m_memorySizeGPR = memory.pinnedRegisters().sizeRegisters[0].sizeRegister;
+        for (const PinnedSizeRegisterInfo& info : memory.pinnedRegisters().sizeRegisters)
             m_proc.pinRegister(info.sizeRegister);
 
         m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned) {
@@ -302,13 +300,13 @@ inline uint32_t sizeOfLoadOp(LoadOpType op)
     case LoadOpType::I32Load:
     case LoadOpType::I64Load32S:
     case LoadOpType::I64Load32U:
+    case LoadOpType::F32Load:
         return 4;
     case LoadOpType::I64Load:
+    case LoadOpType::F64Load:
         return 8;
     case LoadOpType::I32Load16U:
     case LoadOpType::I64Load16U:
-    case LoadOpType::F32Load:
-    case LoadOpType::F64Load:
         break;
     }
     RELEASE_ASSERT_NOT_REACHED();
@@ -659,7 +657,7 @@ void B3IRGenerator::dump(const Vector<ControlEntry>& controlStack, const Express
     dataLogLn("\n");
 }
 
-static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, const Signature* signature, MacroAssemblerCodePtr mainFunction, Memory* memory)
+static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, const Signature* signature, MacroAssemblerCodePtr mainFunction, MemoryInformation& memory)
 {
     Procedure proc;
     BasicBlock* block = proc.addBlock();
@@ -681,12 +679,13 @@ static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, const Signatur
     // Move memory values to the approriate places, if needed.
     Value* baseMemory = nullptr;
     Vector<Value*> sizes;
-    if (memory) {
-        baseMemory = block->appendNew<ConstPtrValue>(proc, Origin(), memory->memory());
+    if (!!memory) {
+        baseMemory = block->appendNew<MemoryValue>(proc, Load, Int64, Origin(),
+            block->appendNew<ConstPtrValue>(proc, Origin(), &vm.topWasmMemoryPointer));
         Value* size = block->appendNew<MemoryValue>(proc, Load, Int32, Origin(),
-            block->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<char*>(memory) + Memory::offsetOfSize()));
-        sizes.reserveCapacity(memory->pinnedRegisters().sizeRegisters.size());
-        for (auto info : memory->pinnedRegisters().sizeRegisters) {
+            block->appendNew<ConstPtrValue>(proc, Origin(), &vm.topWasmMemorySize));
+        sizes.reserveCapacity(memory.pinnedRegisters().sizeRegisters.size());
+        for (auto info : memory.pinnedRegisters().sizeRegisters) {
             sizes.append(block->appendNew<Value>(proc, Sub, Origin(), size,
                 block->appendNew<Const32Value>(proc, Origin(), info.sizeOffset)));
         }
@@ -700,11 +699,11 @@ static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, const Signatur
 
     // Move the arguments into place.
     Value* result = wasmCallingConvention().setupCall(proc, block, Origin(), arguments, toB3Type(signature->returnType), [&] (PatchpointValue* patchpoint) {
-        if (memory) {
-            ASSERT(sizes.size() == memory->pinnedRegisters().sizeRegisters.size());
-            patchpoint->append(ConstrainedValue(baseMemory, ValueRep::reg(memory->pinnedRegisters().baseMemoryPointer)));
+        if (!!memory) {
+            ASSERT(sizes.size() == memory.pinnedRegisters().sizeRegisters.size());
+            patchpoint->append(ConstrainedValue(baseMemory, ValueRep::reg(memory.pinnedRegisters().baseMemoryPointer)));
             for (unsigned i = 0; i < sizes.size(); ++i)
-                patchpoint->append(ConstrainedValue(sizes[i], ValueRep::reg(memory->pinnedRegisters().sizeRegisters[i].sizeRegister)));
+                patchpoint->append(ConstrainedValue(sizes[i], ValueRep::reg(memory.pinnedRegisters().sizeRegisters[i].sizeRegister)));
         }
 
         patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
@@ -738,7 +737,7 @@ static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, const Signatur
     return std::make_unique<Compilation>(vm, proc);
 }
 
-std::unique_ptr<WasmInternalFunction> parseAndCompile(VM& vm, const uint8_t* functionStart, size_t functionLength, Memory* memory, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const FunctionIndexSpace& functionIndexSpace, unsigned optLevel)
+std::unique_ptr<WasmInternalFunction> parseAndCompile(VM& vm, const uint8_t* functionStart, size_t functionLength, MemoryInformation& memory, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const FunctionIndexSpace& functionIndexSpace, unsigned optLevel)
 {
     auto result = std::make_unique<WasmInternalFunction>();
 
index 794b5cc..042007e 100644 (file)
@@ -35,9 +35,9 @@ extern "C" void dumpProcedure(void*);
 
 namespace JSC { namespace Wasm {
 
-class Memory;
+class MemoryInformation;
 
-std::unique_ptr<WasmInternalFunction> parseAndCompile(VM&, const uint8_t*, size_t, Memory*, const Signature*, Vector<UnlinkedWasmToWasmCall>&, const FunctionIndexSpace&, unsigned optLevel = 1);
+std::unique_ptr<WasmInternalFunction> parseAndCompile(VM&, const uint8_t*, size_t, MemoryInformation&, const Signature*, Vector<UnlinkedWasmToWasmCall>&, const FunctionIndexSpace&, unsigned optLevel = 1);
 
 } } // namespace JSC::Wasm
 
index 267c54b..5e0a61b 100644 (file)
@@ -33,8 +33,6 @@
 
 namespace JSC { namespace Wasm {
 
-ModuleInformation::~ModuleInformation() { }
-
 } } // namespace JSC::Wasm
 
 #endif // ENABLE(WEBASSEMBLY)
index 43b119a..8383d4c 100644 (file)
@@ -32,7 +32,9 @@
 #include "CodeLocation.h"
 #include "Identifier.h"
 #include "MacroAssemblerCodeRef.h"
+#include "WasmMemoryInformation.h"
 #include "WasmOps.h"
+#include "WasmPageCount.h"
 #include <wtf/Vector.h>
 
 namespace JSC {
@@ -95,15 +97,13 @@ struct Import {
     unsigned kindIndex; // Index in the vector of the corresponding kind.
 };
 
-class Memory;
-
 struct Export {
     Identifier field;
     External::Kind kind;
     union {
         uint32_t functionIndex;
         // FIXME implement Table https://bugs.webkit.org/show_bug.cgi?id=164135
-        // FIXME implement Memory https://bugs.webkit.org/show_bug.cgi?id=164134
+        // FIXME implement Memory https://bugs.webkit.org/show_bug.cgi?id=165671
         // FIXME implement Global https://bugs.webkit.org/show_bug.cgi?id=164133
     };
 };
@@ -119,13 +119,10 @@ struct ModuleInformation {
     Vector<Import> imports;
     Vector<Signature*> importFunctions;
     // FIXME implement import Table https://bugs.webkit.org/show_bug.cgi?id=164135
-    // FIXME implement import Memory https://bugs.webkit.org/show_bug.cgi?id=164134
     // FIXME implement import Global https://bugs.webkit.org/show_bug.cgi?id=164133
     Vector<Signature*> internalFunctionSignatures;
-    std::unique_ptr<Memory> memory;
+    MemoryInformation memory;
     Vector<Export> exports;
-
-    ~ModuleInformation();
 };
 
 struct UnlinkedWasmToWasmCall {
index acd133a..6b97b0c 100644 (file)
 
 namespace JSC { namespace Wasm {
 
-Memory::Memory(uint32_t startingSize, uint32_t capacity, const Vector<unsigned>& pinnedSizeRegisters)
+Memory::Memory(PageCount initial, PageCount maximum)
     : m_mode(Mode::BoundsChecking)
-    , m_size(startingSize)
-    , m_capacity(capacity)
+    , m_size(initial.bytes())
+    , m_capacity(maximum ? maximum.bytes() : PageCount::max().bytes())
+    , m_initial(initial)
+    , m_maximum(maximum)
     // FIXME: If we add signal based bounds checking then we need extra space for overflow on load.
     // see: https://bugs.webkit.org/show_bug.cgi?id=162693
-    , m_mappedCapacity(static_cast<uint64_t>(maxPageCount) * static_cast<uint64_t>(pageSize))
+    , m_mappedCapacity(PageCount::max().bytes())
 {
-    ASSERT(pinnedSizeRegisters.size() > 0);
+    RELEASE_ASSERT(!maximum || maximum >= initial); // This should be guaranteed by our caller.
 
     // FIXME: It would be nice if we had a VM tag for wasm memory. https://bugs.webkit.org/show_bug.cgi?id=163600
     void* result = mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
@@ -50,25 +52,12 @@ Memory::Memory(uint32_t startingSize, uint32_t capacity, const Vector<unsigned>&
             return;
     }
 
-    ASSERT(startingSize <= m_mappedCapacity);
-    if (mprotect(result, startingSize, PROT_READ | PROT_WRITE)) {
+    ASSERT(m_size <= m_mappedCapacity);
+    if (mprotect(result, m_size, PROT_READ | PROT_WRITE)) {
         munmap(result, m_mappedCapacity);
         return;
     }
 
-    unsigned remainingPinnedRegisters = pinnedSizeRegisters.size() + 1;
-    jscCallingConvention().m_calleeSaveRegisters.forEach([&] (Reg reg) {
-        GPRReg gpr = reg.gpr();
-        if (!remainingPinnedRegisters || RegisterSet::stackRegisters().get(reg))
-            return;
-        if (remainingPinnedRegisters == 1) {
-            m_pinnedRegisters.baseMemoryPointer = gpr;
-            remainingPinnedRegisters--;
-        } else
-            m_pinnedRegisters.sizeRegisters.append({ gpr, pinnedSizeRegisters[--remainingPinnedRegisters - 1] });
-    });
-
-    ASSERT(!remainingPinnedRegisters);
     m_memory = result;
 }
 
index 7b20a04..946e5e0 100644 (file)
 #if ENABLE(WEBASSEMBLY)
 
 #include "WasmCallingConvention.h"
+#include "WasmPageCount.h"
 
 #include <wtf/Vector.h>
 
 namespace JSC { namespace Wasm {
 
-struct PinnedSizeRegisterInfo {
-    GPRReg sizeRegister;
-    unsigned sizeOffset;
-};
-
-// FIXME: We should support more than one memory size register. Right now we take a vector with only one
-// entry. Specifically an entry where the sizeOffset == 0. If we have more than one size register,
-// we can have one for each load size class. see: https://bugs.webkit.org/show_bug.cgi?id=162952
-struct PinnedRegisterInfo {
-    Vector<PinnedSizeRegisterInfo> sizeRegisters;
-    GPRReg baseMemoryPointer;
-};
-
-constexpr uint32_t pageSize = 64 * KB;
-constexpr uint32_t maxPageCount = static_cast<uint32_t>((1ull << 32) / pageSize);
-
 class Memory {
     WTF_MAKE_NONCOPYABLE(Memory);
     WTF_MAKE_FAST_ALLOCATED;
@@ -59,8 +44,7 @@ public:
         BoundsChecking
     };
 
-    Memory() = default;
-    Memory(uint32_t startingSize, uint32_t capacity, const Vector<unsigned>& pinnedSizeRegisters);
+    JS_EXPORT_PRIVATE Memory(PageCount initial, PageCount maximum);
 
     ~Memory()
     {
@@ -70,10 +54,12 @@ public:
 
     void* memory() const { return m_memory; }
     uint32_t size() const { return m_size; }
-    const PinnedRegisterInfo& pinnedRegisters() const { return m_pinnedRegisters; }
 
     Mode mode() const { return m_mode; }
 
+    PageCount initial() const { return m_initial; }
+    PageCount maximum() const { return m_maximum; }
+
     bool grow(uint32_t newSize)
     {
         ASSERT(m_memory);
@@ -84,13 +70,15 @@ public:
     }
 
     static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(Memory, m_size); }
+
     
 private:
     void* m_memory { nullptr };
-    PinnedRegisterInfo m_pinnedRegisters;
     Mode m_mode;
     uint32_t m_size { 0 };
     uint32_t m_capacity { 0 };
+    PageCount m_initial;
+    PageCount m_maximum;
     uint64_t m_mappedCapacity { 0 };
 };
 
diff --git a/Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp b/Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp
new file mode 100644 (file)
index 0000000..2e1d30a
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WasmMemoryInformation.h"
+
+#include "WasmCallingConvention.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+namespace JSC { namespace Wasm {
+
+MemoryInformation::MemoryInformation(PageCount initial, PageCount maximum,  const Vector<unsigned>& pinnedSizeRegisters, bool isImport)
+    : m_initial(initial)
+    , m_maximum(maximum)
+    , m_isImport(isImport)
+{
+    RELEASE_ASSERT(!!m_initial);
+    RELEASE_ASSERT(!m_maximum || m_maximum >= m_initial);
+    ASSERT(!!*this);
+
+    unsigned remainingPinnedRegisters = pinnedSizeRegisters.size() + 1;
+    jscCallingConvention().m_calleeSaveRegisters.forEach([&] (Reg reg) {
+        GPRReg gpr = reg.gpr();
+        if (!remainingPinnedRegisters || RegisterSet::stackRegisters().get(reg))
+            return;
+        if (remainingPinnedRegisters == 1) {
+            m_pinnedRegisters.baseMemoryPointer = gpr;
+            remainingPinnedRegisters--;
+        } else
+            m_pinnedRegisters.sizeRegisters.append({ gpr, pinnedSizeRegisters[--remainingPinnedRegisters - 1] });
+    });
+
+    ASSERT(!remainingPinnedRegisters);
+}
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/WasmMemoryInformation.h b/Source/JavaScriptCore/wasm/WasmMemoryInformation.h
new file mode 100644 (file)
index 0000000..ee63d9e
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "GPRInfo.h"
+#include "WasmPageCount.h"
+#include <wtf/Vector.h>
+
+namespace JSC { namespace Wasm {
+
+struct PinnedSizeRegisterInfo {
+    GPRReg sizeRegister;
+    unsigned sizeOffset;
+};
+
+// FIXME: We should support more than one memory size register. Right now we take a vector with only one
+// entry. Specifically an entry where the sizeOffset == 0. If we have more than one size register,
+// we can have one for each load size class. see: https://bugs.webkit.org/show_bug.cgi?id=162952
+struct PinnedRegisterInfo {
+    Vector<PinnedSizeRegisterInfo> sizeRegisters;
+    GPRReg baseMemoryPointer;
+};
+
+class MemoryInformation {
+public:
+    MemoryInformation()
+    {
+        ASSERT(!*this);
+    }
+
+    MemoryInformation(PageCount initial, PageCount maximum, const Vector<unsigned>& pinnedSizeRegisters, bool isImport);
+
+    const PinnedRegisterInfo& pinnedRegisters() const { return m_pinnedRegisters; }
+    PageCount initial() const { return m_initial; }
+    PageCount maximum() const { return m_maximum; }
+    bool isImport() const { return m_isImport; }
+
+    explicit operator bool() const { return !!m_initial; }
+
+private:
+    PageCount m_initial { };
+    PageCount m_maximum { };
+    PinnedRegisterInfo m_pinnedRegisters { };
+    bool m_isImport { false };
+};
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WASM)
index af133a4..37298f8 100644 (file)
@@ -30,7 +30,7 @@
 
 #include "IdentifierInlines.h"
 #include "WasmFormat.h"
-#include "WasmMemory.h"
+#include "WasmMemoryInformation.h"
 #include "WasmOps.h"
 #include "WasmSections.h"
 
@@ -256,7 +256,9 @@ bool ModuleParser::parseImport()
             break;
         }
         case External::Memory: {
-            // FIXME https://bugs.webkit.org/show_bug.cgi?id=164134
+            bool isImport = true;
+            if (!parseMemoryHelper(isImport))
+                return false;
             break;
         }
         case External::Global: {
@@ -305,36 +307,59 @@ bool ModuleParser::parseTable()
     return true;
 }
 
-bool ModuleParser::parseMemory()
+bool ModuleParser::parseMemoryHelper(bool isImport)
 {
-    uint8_t count;
-    if (!parseVarUInt1(count))
+    // We don't allow redeclaring memory. Either via import or definition.
+    if (m_module->memory)
         return false;
 
-    if (!count)
-        return true;
-
     uint8_t flags;
-    uint32_t size;
-    if (!parseVarUInt1(flags)
-        || !parseVarUInt32(size)
-        || size > maxPageCount)
+    if (!parseVarUInt1(flags))
+        return false;
+
+    uint32_t initial;
+    if (!parseVarUInt32(initial))
+        return false;
+
+    if (!PageCount::isValid(initial))
         return false;
 
-    uint32_t capacity = maxPageCount;
+    PageCount initialPageCount(initial);
+
+    PageCount maximumPageCount;
     if (flags) {
-        if (!parseVarUInt32(capacity)
-            || size > capacity
-            || capacity > maxPageCount)
+        uint32_t maximum;
+        if (!parseVarUInt32(maximum))
             return false;
-    }
 
-    capacity *= pageSize;
-    size *= pageSize;
+        if (!PageCount::isValid(maximum))
+            return false;
+
+        maximumPageCount = PageCount(maximum);
+        if (initialPageCount > maximumPageCount)
+            return false;
+    }
 
     Vector<unsigned> pinnedSizes = { 0 };
-    m_module->memory = std::make_unique<Memory>(size, capacity, pinnedSizes);
-    return m_module->memory->memory();
+    m_module->memory = MemoryInformation(initialPageCount, maximumPageCount, pinnedSizes, isImport);
+    return true;
+}
+
+bool ModuleParser::parseMemory()
+{
+    uint8_t count;
+    if (!parseVarUInt1(count))
+        return false;
+
+    if (!count)
+        return true;
+
+    // We only allow one memory for now.
+    if (count != 1)
+        return false;
+
+    bool isImport = false;
+    return parseMemoryHelper(isImport);
 }
 
 bool ModuleParser::parseGlobal()
@@ -372,7 +397,7 @@ bool ModuleParser::parseExport()
             break;
         }
         case External::Memory: {
-            // FIXME https://bugs.webkit.org/show_bug.cgi?id=164134
+            // FIXME: https://bugs.webkit.org/show_bug.cgi?id=165671
             break;
         }
         case External::Global: {
index 9262994..3007f67 100644 (file)
@@ -78,6 +78,8 @@ private:
     FOR_EACH_WASM_SECTION(WASM_SECTION_DECLARE_PARSER)
 #undef WASM_SECTION_DECLARE_PARSER
 
+    bool WARN_UNUSED_RETURN parseMemoryHelper(bool isImport);
+
     VM* m_vm;
     std::unique_ptr<ModuleInformation> m_module;
     FunctionIndexSpace m_functionIndexSpace;
diff --git a/Source/JavaScriptCore/wasm/WasmPageCount.h b/Source/JavaScriptCore/wasm/WasmPageCount.h
new file mode 100644 (file)
index 0000000..6a0668f
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+namespace JSC { namespace Wasm {
+
+class PageCount {
+
+public:
+    PageCount()
+        : m_pageCount(UINT_MAX)
+    {
+        static_assert(maxPageCount < UINT_MAX, "We rely on this here.");
+    }
+
+    PageCount(uint32_t pageCount)
+        : m_pageCount(pageCount)
+    { }
+
+    size_t bytes() { return m_pageCount * pageSize; }
+
+    static bool isValid(uint32_t pageCount)
+    {
+        return pageCount <= maxPageCount;
+    }
+
+    static PageCount max()
+    {
+        return PageCount(maxPageCount);
+    }
+
+    explicit operator bool() const
+    {
+        return m_pageCount != UINT_MAX;
+    }
+
+    bool operator<(const PageCount& other) const { return m_pageCount < other.m_pageCount; }
+    bool operator>(const PageCount& other) const { return m_pageCount > other.m_pageCount; }
+    bool operator>=(const PageCount& other) const { return m_pageCount >= other.m_pageCount; }
+
+private:
+    static constexpr uint32_t pageSize = 64 * KB;
+    static constexpr uint32_t maxPageCount = static_cast<uint32_t>((1ull << 32) / pageSize);
+
+    uint32_t m_pageCount;
+};
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WASM)
index 7a163d7..d89a1b3 100644 (file)
@@ -116,7 +116,7 @@ void Plan::run()
         unsigned functionIndexSpace = m_wasmToJSStubs.size() + functionIndex;
         ASSERT(m_functionIndexSpace[functionIndexSpace].signature == signature);
 
-        String error = validateFunction(functionStart, functionLength, signature, m_functionIndexSpace);
+        String error = validateFunction(functionStart, functionLength, signature, m_functionIndexSpace, m_moduleInformation->memory);
         if (!error.isNull()) {
             if (verbose) {
                 for (unsigned i = 0; i < functionLength; ++i)
@@ -128,7 +128,7 @@ void Plan::run()
         }
 
         unlinkedWasmToWasmCalls.uncheckedAppend(Vector<UnlinkedWasmToWasmCall>());
-        m_wasmInternalFunctions.uncheckedAppend(parseAndCompile(*m_vm, functionStart, functionLength, m_moduleInformation->memory.get(), signature, unlinkedWasmToWasmCalls.at(functionIndex), m_functionIndexSpace));
+        m_wasmInternalFunctions.uncheckedAppend(parseAndCompile(*m_vm, functionStart, functionLength, m_moduleInformation->memory, signature, unlinkedWasmToWasmCalls.at(functionIndex), m_functionIndexSpace));
         m_functionIndexSpace[functionIndexSpace].code = m_wasmInternalFunctions[functionIndex]->code->code().executableAddress();
     }
 
index 7749270..9b0c1b2 100644 (file)
@@ -42,8 +42,6 @@ class JSWebAssemblyCallee;
 
 namespace Wasm {
 
-class Memory;
-
 class Plan {
 public:
     JS_EXPORT_PRIVATE Plan(VM*, Vector<uint8_t>);
@@ -67,17 +65,12 @@ public:
         return m_moduleInformation->exports;
     }
 
-    const Memory* memory() const
-    {
-        RELEASE_ASSERT(!failed());
-        return m_moduleInformation->memory.get();
-    }
-
     size_t internalFunctionCount() const
     {
         RELEASE_ASSERT(!failed());
         return m_wasmInternalFunctions.size();
     }
+
     B3::Compilation* jsToWasmEntryPointForFunction(size_t i) const
     {
         ASSERT(i > m_wasmToJSStubs.size());
index e14162b..a024885 100644 (file)
@@ -115,10 +115,13 @@ public:
 
     void dump(const Vector<ControlEntry>& controlStack, const ExpressionList& expressionStack);
 
+    bool hasMemory() const { return !!m_memory; }
+
     void setErrorMessage(String&& message) { ASSERT(m_errorMessage.isNull()); m_errorMessage = WTFMove(message); }
     String errorMessage() const { return m_errorMessage; }
-    Validate(ExpressionType returnType)
+    Validate(ExpressionType returnType, const MemoryInformation& memory)
         : m_returnType(returnType)
+        , m_memory(memory)
     {
     }
 
@@ -131,6 +134,7 @@ private:
     ExpressionType m_returnType;
     Vector<Type> m_locals;
     String m_errorMessage;
+    const MemoryInformation& m_memory;
 };
 
 bool Validate::addArguments(const Vector<Type>& args)
@@ -367,9 +371,9 @@ void Validate::dump(const Vector<ControlEntry>&, const ExpressionList&)
     // Think of this as penance for the sin of bad error messages.
 }
 
-String validateFunction(const uint8_t* source, size_t length, const Signature* signature, const FunctionIndexSpace& functionIndexSpace)
+String validateFunction(const uint8_t* source, size_t length, const Signature* signature, const FunctionIndexSpace& functionIndexSpace, const MemoryInformation& memory)
 {
-    Validate context(signature->returnType);
+    Validate context(signature->returnType, memory);
     FunctionParser<Validate> validator(context, source, length, signature, functionIndexSpace);
     if (!validator.parse()) {
         // FIXME: add better location information here. see: https://bugs.webkit.org/show_bug.cgi?id=164288
index df43fa5..454f9f5 100644 (file)
@@ -31,7 +31,7 @@
 
 namespace JSC { namespace Wasm {
 
-String validateFunction(const uint8_t*, size_t, const Signature*, const FunctionIndexSpace&);
+String validateFunction(const uint8_t*, size_t, const Signature*, const FunctionIndexSpace&, const MemoryInformation&);
 
 } } // namespace JSC::Wasm
 
index bbe51e1..25a392e 100755 (executable)
@@ -143,6 +143,9 @@ namespace JSC { namespace Wasm {
 
 bool Validate::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t)
 {
+    if (!hasMemory())
+        return false;
+
     switch (op) {
 """ + loadCases + """
     }
@@ -150,6 +153,9 @@ bool Validate::load(LoadOpType op, ExpressionType pointer, ExpressionType& resul
 
 bool Validate::store(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t)
 {
+    if (!hasMemory())
+        return false;
+
     switch (op) {
 """ + storeCases + """
     }
index f430074..3dbd1b6 100644 (file)
@@ -32,6 +32,7 @@
 #include "JSCInlines.h"
 #include "JSModuleEnvironment.h"
 #include "JSModuleNamespaceObject.h"
+#include "JSWebAssemblyMemory.h"
 #include "JSWebAssemblyModule.h"
 #include <wtf/StdLibExtras.h>
 
@@ -78,6 +79,7 @@ void JSWebAssemblyInstance::visitChildren(JSCell* cell, SlotVisitor& visitor)
     Base::visitChildren(thisObject, visitor);
     visitor.append(&thisObject->m_module);
     visitor.append(&thisObject->m_moduleNamespaceObject);
+    visitor.append(&thisObject->m_memory);
     for (unsigned i = 0; i < thisObject->m_numImportFunctions; ++i)
         visitor.append(thisObject->importFunction(i));
 }
index 5ed2598..6cb5a71 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "JSDestructibleObject.h"
 #include "JSObject.h"
+#include "JSWebAssemblyMemory.h"
 
 namespace JSC {
 
@@ -67,30 +68,34 @@ public:
         importFunction(idx)->set(vm, this, value);
     }
 
-    static size_t offsetOfImportFunctions()
-    {
-        return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSCell>)>(sizeof(JSWebAssemblyInstance));
-    }
+    JSWebAssemblyMemory* memory() { return m_memory.get(); }
+    void setMemory(VM& vm, JSWebAssemblyMemory* memory) { m_memory.set(vm, this, memory); }
 
     static size_t offsetOfImportFunction(unsigned idx)
     {
         return offsetOfImportFunctions() + sizeof(WriteBarrier<JSCell>) * idx;
     }
 
-    static size_t allocationSize(unsigned numImportFunctions)
-    {
-        return offsetOfImportFunctions() + sizeof(WriteBarrier<JSCell>) * numImportFunctions;
-    }
-
 protected:
     JSWebAssemblyInstance(VM&, Structure*, unsigned);
     void finishCreation(VM&, JSWebAssemblyModule*, JSModuleNamespaceObject*);
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
 
+    static size_t offsetOfImportFunctions()
+    {
+        return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSCell>)>(sizeof(JSWebAssemblyInstance));
+    }
+
+    static size_t allocationSize(unsigned numImportFunctions)
+    {
+        return offsetOfImportFunctions() + sizeof(WriteBarrier<JSCell>) * numImportFunctions;
+    }
+
 private:
     WriteBarrier<JSWebAssemblyModule> m_module;
     WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
+    WriteBarrier<JSWebAssemblyMemory> m_memory;
     unsigned m_numImportFunctions;
 };
 
index d56a2c7..681d221 100644 (file)
 
 #include "JSCInlines.h"
 
+#include "JSArrayBuffer.h"
+#include "ArrayBuffer.h"
+
 namespace JSC {
 
-JSWebAssemblyMemory* JSWebAssemblyMemory::create(VM& vm, Structure* structure)
+const ClassInfo JSWebAssemblyMemory::s_info = { "WebAssembly.Memory", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyMemory) };
+
+JSWebAssemblyMemory* JSWebAssemblyMemory::create(VM& vm, Structure* structure, std::unique_ptr<Wasm::Memory>&& memory)
 {
-    auto* instance = new (NotNull, allocateCell<JSWebAssemblyMemory>(vm.heap)) JSWebAssemblyMemory(vm, structure);
+    auto* instance = new (NotNull, allocateCell<JSWebAssemblyMemory>(vm.heap)) JSWebAssemblyMemory(vm, structure, WTFMove(memory));
     instance->finishCreation(vm);
     return instance;
 }
@@ -44,11 +49,28 @@ Structure* JSWebAssemblyMemory::createStructure(VM& vm, JSGlobalObject* globalOb
     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
 }
 
-JSWebAssemblyMemory::JSWebAssemblyMemory(VM& vm, Structure* structure)
+JSWebAssemblyMemory::JSWebAssemblyMemory(VM& vm, Structure* structure, std::unique_ptr<Wasm::Memory>&& memory)
     : Base(vm, structure)
+    , m_memory(WTFMove(memory))
 {
 }
 
+JSArrayBuffer* JSWebAssemblyMemory::buffer(VM& vm, JSGlobalObject* globalObject)
+{
+    if (m_bufferWrapper)
+        return m_bufferWrapper.get();
+
+    auto destructor = [] (void*) {
+        // We don't need to do anything here to destroy the memory.
+        // The ArrayBuffer backing the JSArrayBuffer is only owned by us,
+        // so we guarantee its lifecylce.
+    };
+    m_buffer = ArrayBuffer::createFromBytes(memory()->memory(), memory()->size(), WTFMove(destructor));
+    m_bufferWrapper.set(vm, this, JSArrayBuffer::create(vm, globalObject->m_arrayBufferStructure.get(), m_buffer.get()));
+    RELEASE_ASSERT(m_bufferWrapper);
+    return m_bufferWrapper.get();
+}
+
 void JSWebAssemblyMemory::finishCreation(VM& vm)
 {
     Base::finishCreation(vm);
@@ -66,10 +88,9 @@ void JSWebAssemblyMemory::visitChildren(JSCell* cell, SlotVisitor& visitor)
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
 
     Base::visitChildren(thisObject, visitor);
+    visitor.append(&thisObject->m_bufferWrapper);
 }
 
-const ClassInfo JSWebAssemblyMemory::s_info = { "WebAssembly.Memory", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyMemory) };
-
 } // namespace JSC
 
 #endif // ENABLE(WEBASSEMBLY)
index b36187b..1d43c95 100644 (file)
 
 #include "JSDestructibleObject.h"
 #include "JSObject.h"
+#include "WasmMemory.h"
+#include <wtf/RefPtr.h>
 
 namespace JSC {
 
+class ArrayBuffer;
+class JSArrayBuffer;
+
 class JSWebAssemblyMemory : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static JSWebAssemblyMemory* create(VM&, Structure*);
+    static JSWebAssemblyMemory* create(VM&, Structure*, std::unique_ptr<Wasm::Memory>&&);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
 
+    Wasm::Memory* memory() { return m_memory.get(); }
+    JSArrayBuffer* buffer(VM& vm, JSGlobalObject*);
+
 protected:
-    JSWebAssemblyMemory(VM&, Structure*);
+    JSWebAssemblyMemory(VM&, Structure*, std::unique_ptr<Wasm::Memory>&&);
     void finishCreation(VM&);
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
+
+    std::unique_ptr<Wasm::Memory> m_memory;
+    WriteBarrier<JSArrayBuffer> m_bufferWrapper;
+    RefPtr<ArrayBuffer> m_buffer;
 };
 
 } // namespace JSC
index da454a1..1947731 100644 (file)
 #include "JSObject.h"
 #include "JSWebAssemblyCallee.h"
 #include "JSWebAssemblyInstance.h"
+#include "JSWebAssemblyMemory.h"
 #include "LLIntThunks.h"
 #include "ProtoCallFrame.h"
 #include "VM.h"
 #include "WasmFormat.h"
+#include "WasmMemory.h"
 
 namespace JSC {
 
@@ -90,6 +92,16 @@ static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
         argCount = boxedArgs.size();
     }
 
+    // Setup the memory that the entrance loads.
+    if (JSWebAssemblyMemory* memory = wasmFunction->instance()->memory()) {
+        Wasm::Memory* wasmMemory = memory->memory();
+        vm.topWasmMemoryPointer = wasmMemory->memory();
+        vm.topWasmMemorySize = wasmMemory->size();
+    } else {
+        vm.topWasmMemoryPointer = nullptr;
+        vm.topWasmMemorySize = 0;
+    }
+
     // Note: we specifically use the WebAsseblyFunction as the callee to begin with in the ProtoCallFrame.
     // The reason for this is that calling into the llint may stack overflow, and the stack overflow
     // handler might read the global object from the callee. The JSWebAssemblyCallee doesn't have a
index f3b7fde..791fd37 100644 (file)
@@ -33,6 +33,7 @@
 #include "JSModuleEnvironment.h"
 #include "JSModuleNamespaceObject.h"
 #include "JSWebAssemblyInstance.h"
+#include "JSWebAssemblyMemory.h"
 #include "JSWebAssemblyModule.h"
 #include "WebAssemblyFunction.h"
 #include "WebAssemblyInstancePrototype.h"
@@ -51,64 +52,66 @@ const ClassInfo WebAssemblyInstanceConstructor::s_info = { "Function", &Base::s_
  @end
  */
 
-static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* state)
+static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* exec)
 {
-    auto& vm = state->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    auto* globalObject = state->lexicalGlobalObject();
+    auto& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+    auto* globalObject = exec->lexicalGlobalObject();
 
     // If moduleObject is not a WebAssembly.Module instance, a TypeError is thrown.
-    JSWebAssemblyModule* jsModule = jsDynamicCast<JSWebAssemblyModule*>(state->argument(0));
+    JSWebAssemblyModule* jsModule = jsDynamicCast<JSWebAssemblyModule*>(exec->argument(0));
     if (!jsModule)
-        return JSValue::encode(throwException(state, scope, createTypeError(state, ASCIILiteral("first argument to WebAssembly.Instance must be a WebAssembly.Module"), defaultSourceAppender, runtimeTypeForValue(state->argument(0)))));
+        return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("first argument to WebAssembly.Instance must be a WebAssembly.Module"), defaultSourceAppender, runtimeTypeForValue(exec->argument(0)))));
     const Wasm::ModuleInformation& moduleInformation = jsModule->moduleInformation();
 
     // If the importObject parameter is not undefined and Type(importObject) is not Object, a TypeError is thrown.
-    JSValue importArgument = state->argument(1);
+    JSValue importArgument = exec->argument(1);
     JSObject* importObject = importArgument.getObject();
     if (!importArgument.isUndefined() && !importObject)
-        return JSValue::encode(throwException(state, scope, createTypeError(state, ASCIILiteral("second argument to WebAssembly.Instance must be undefined or an Object"), defaultSourceAppender, runtimeTypeForValue(importArgument))));
+        return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("second argument to WebAssembly.Instance must be undefined or an Object"), defaultSourceAppender, runtimeTypeForValue(importArgument))));
 
     // If the list of module.imports is not empty and Type(importObject) is not Object, a TypeError is thrown.
     if (moduleInformation.imports.size() && !importObject)
-        return JSValue::encode(throwException(state, scope, createTypeError(state, ASCIILiteral("second argument to WebAssembly.Instance must be Object because the WebAssembly.Module has imports"), defaultSourceAppender, runtimeTypeForValue(importArgument))));
+        return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("second argument to WebAssembly.Instance must be Object because the WebAssembly.Module has imports"), defaultSourceAppender, runtimeTypeForValue(importArgument))));
 
     Identifier moduleKey = Identifier::fromUid(PrivateName(PrivateName::Description, "WebAssemblyInstance"));
-    WebAssemblyModuleRecord* moduleRecord = WebAssemblyModuleRecord::create(state, vm, globalObject->webAssemblyModuleRecordStructure(), moduleKey, moduleInformation);
-    RETURN_IF_EXCEPTION(scope, { });
+    WebAssemblyModuleRecord* moduleRecord = WebAssemblyModuleRecord::create(exec, vm, globalObject->webAssemblyModuleRecordStructure(), moduleKey, moduleInformation);
+    RETURN_IF_EXCEPTION(throwScope, { });
 
-    Structure* instanceStructure = InternalFunction::createSubclassStructure(state, state->newTarget(), globalObject->WebAssemblyInstanceStructure());
-    RETURN_IF_EXCEPTION(scope, { });
+    Structure* instanceStructure = InternalFunction::createSubclassStructure(exec, exec->newTarget(), globalObject->WebAssemblyInstanceStructure());
+    RETURN_IF_EXCEPTION(throwScope, { });
 
-    JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, instanceStructure, jsModule, moduleRecord->getModuleNamespace(state), moduleInformation.imports.size());
-    RETURN_IF_EXCEPTION(scope, { });
+    JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, instanceStructure, jsModule, moduleRecord->getModuleNamespace(exec), moduleInformation.imports.size());
+    RETURN_IF_EXCEPTION(throwScope, { });
 
     // Let funcs, memories and tables be initially-empty lists of callable JavaScript objects, WebAssembly.Memory objects and WebAssembly.Table objects, respectively.
     // Let imports be an initially-empty list of external values.
     unsigned numImportFunctions = 0;
 
     // FIXME implement Table https://bugs.webkit.org/show_bug.cgi?id=164135
-    // FIXME implement Memory https://bugs.webkit.org/show_bug.cgi?id=164134
     // FIXME implement Global https://bugs.webkit.org/show_bug.cgi?id=164133
 
+    bool hasMemoryImport = false;
     // For each import i in module.imports:
     for (auto& import : moduleInformation.imports) {
         // 1. Let o be the resultant value of performing Get(importObject, i.module_name).
-        JSValue importModuleValue = importObject->get(state, import.module);
-        RETURN_IF_EXCEPTION(scope, { });
+        JSValue importModuleValue = importObject->get(exec, import.module);
+        RETURN_IF_EXCEPTION(throwScope, { });
         // 2. If Type(o) is not Object, throw a TypeError.
         if (!importModuleValue.isObject())
-            return JSValue::encode(throwException(state, scope, createTypeError(state, ASCIILiteral("import must be an object"), defaultSourceAppender, runtimeTypeForValue(importModuleValue))));
+            return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("import must be an object"), defaultSourceAppender, runtimeTypeForValue(importModuleValue))));
+
         // 3. Let v be the value of performing Get(o, i.item_name)
         JSObject* object = jsCast<JSObject*>(importModuleValue);
-        JSValue value = object->get(state, import.field);
-        RETURN_IF_EXCEPTION(scope, { });
+        JSValue value = object->get(exec, import.field);
+        RETURN_IF_EXCEPTION(throwScope, { });
+
         switch (import.kind) {
         case Wasm::External::Function: {
             // 4. If i is a function import:
             // i. If IsCallable(v) is false, throw a TypeError.
             if (!value.isFunction())
-                return JSValue::encode(throwException(state, scope, createTypeError(state, ASCIILiteral("import function must be callable"), defaultSourceAppender, runtimeTypeForValue(value))));
+                return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("import function must be callable"), defaultSourceAppender, runtimeTypeForValue(value))));
             JSCell* cell = value.asCell();
             // ii. If v is an Exported Function Exotic Object:
             if (WebAssemblyFunction* importedExports = jsDynamicCast<WebAssemblyFunction*>(object)) {
@@ -138,11 +141,34 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* st
         }
         case Wasm::External::Memory: {
             // 6. If i is a memory import:
-            // FIXME implement Memory https://bugs.webkit.org/show_bug.cgi?id=164134
+            RELEASE_ASSERT(!hasMemoryImport); // This should be guaranteed by a validation failure.
+            RELEASE_ASSERT(moduleInformation.memory);
+            hasMemoryImport = true;
+            JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(value);
             // i. If v is not a WebAssembly.Memory object, throw a TypeError.
+            if (!memory)
+                return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("Memory import is not an instance of WebAssembly.Memory"))));
+
+            Wasm::PageCount expectedInitial = moduleInformation.memory.initial();
+            Wasm::PageCount actualInitial = memory->memory()->initial();
+            if (actualInitial < expectedInitial)
+                return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("Memory import provided an 'initial' that is too small"))));
+
+            if (Wasm::PageCount expectedMaximum = moduleInformation.memory.maximum()) {
+                Wasm::PageCount actualMaximum = memory->memory()->maximum();
+                if (!actualMaximum) {
+                    return JSValue::encode(
+                        throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("Memory import did not have a 'maximum' but the module requires that it does"))));
+                }
+
+                if (actualMaximum > expectedMaximum) {
+                    return JSValue::encode(
+                        throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("Memory imports 'maximum' is larger than the module's expected 'maximum"))));
+                }
+            }
             // ii. Append v to memories.
             // iii. Append v.[[Memory]] to imports.
-            RELEASE_ASSERT_NOT_REACHED();
+            instance->setMemory(vm, memory);
             break;
         }
         case Wasm::External::Global: {
@@ -157,13 +183,28 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* st
         }
     }
 
-    moduleRecord->link(state, instance);
-    RETURN_IF_EXCEPTION(scope, { });
+    {
+        if (!!moduleInformation.memory && moduleInformation.memory.isImport()) {
+            // We should either have an import or we should have thrown an exception.
+            RELEASE_ASSERT(hasMemoryImport);
+        }
+
+        if (moduleInformation.memory && !hasMemoryImport) {
+            RELEASE_ASSERT(!moduleInformation.memory.isImport());
+            // We create a memory when it's a memory definition.
+            std::unique_ptr<Wasm::Memory> memory = std::make_unique<Wasm::Memory>(moduleInformation.memory.initial(), moduleInformation.memory.maximum());
+            instance->setMemory(vm,
+               JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory)));
+        }
+    }
+
+    moduleRecord->link(exec, instance);
+    RETURN_IF_EXCEPTION(throwScope, { });
     if (verbose)
         moduleRecord->dump();
-    JSValue startResult = moduleRecord->evaluate(state);
+    JSValue startResult = moduleRecord->evaluate(exec);
     UNUSED_PARAM(startResult);
-    RETURN_IF_EXCEPTION(scope, { });
+    RETURN_IF_EXCEPTION(throwScope, { });
 
     return JSValue::encode(instance);
 }
index cfcac60..0c27f45 100644 (file)
 
 #include "FunctionPrototype.h"
 #include "JSCInlines.h"
+#include "JSWebAssemblyMemory.h"
+#include "WasmMemory.h"
+#include "WasmPageCount.h"
 #include "WebAssemblyMemoryPrototype.h"
+#include <wtf/Optional.h>
 
 #include "WebAssemblyMemoryConstructor.lut.h"
 
@@ -43,19 +47,75 @@ const ClassInfo WebAssemblyMemoryConstructor::s_info = { "Function", &Base::s_in
  @end
  */
 
-static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyMemory(ExecState* state)
+static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyMemory(ExecState* exec)
 {
-    VM& vm = state->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    // FIXME https://bugs.webkit.org/show_bug.cgi?id=164134
-    return JSValue::encode(throwException(state, scope, createError(state, ASCIILiteral("WebAssembly doesn't yet implement the Memory constructor property"))));
+    VM& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+    if (exec->argumentCount() != 1)
+        return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Memory expects exactly one argument"))));
+
+    auto getUint32 = [&] (JSValue value) -> uint32_t {
+        double doubleValue = value.toInteger(exec);
+        RETURN_IF_EXCEPTION(throwScope, { });
+        if (doubleValue < 0 || doubleValue > UINT_MAX) {
+            throwException(exec, throwScope,
+                createRangeError(exec, ASCIILiteral("WebAssembly.Memory expects the 'initial' and 'maximum' properties to be integers in the range: [0, 2^32 - 1]")));
+            return 0;
+        }
+        return static_cast<uint32_t>(doubleValue);
+    };
+
+    JSObject* memoryDescriptor;
+    {
+        JSValue argument = exec->argument(0);
+        if (!argument.isObject())
+            return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Memory expects its first argument to be an object"))));
+        memoryDescriptor = jsCast<JSObject*>(argument);
+    }
+
+    Wasm::PageCount initialPageCount;
+    {
+        Identifier initial = Identifier::fromString(&vm, "initial");
+        JSValue minSizeValue = memoryDescriptor->get(exec, initial);
+        RETURN_IF_EXCEPTION(throwScope, { });
+        uint32_t size = getUint32(minSizeValue);
+        RETURN_IF_EXCEPTION(throwScope, { });
+        if (!Wasm::PageCount::isValid(size))
+            return JSValue::encode(throwException(exec, throwScope, createRangeError(exec, ASCIILiteral("WebAssembly.Memory 'initial' page count is too large"))));
+        initialPageCount = Wasm::PageCount(size);
+    }
+
+    Wasm::PageCount maximumPageCount;
+    {
+        Identifier maximum = Identifier::fromString(&vm, "maximum");
+        bool hasProperty = memoryDescriptor->hasProperty(exec, maximum);
+        RETURN_IF_EXCEPTION(throwScope, { });
+        if (hasProperty) {
+            JSValue maxSizeValue = memoryDescriptor->get(exec, maximum);
+            RETURN_IF_EXCEPTION(throwScope, { });
+            uint32_t size = getUint32(maxSizeValue);
+            if (!Wasm::PageCount::isValid(size))
+                return JSValue::encode(throwException(exec, throwScope, createRangeError(exec, ASCIILiteral("WebAssembly.Memory 'maximum' page count is too large"))));
+            RETURN_IF_EXCEPTION(throwScope, { });
+            maximumPageCount = Wasm::PageCount(size);
+
+            if (initialPageCount > maximumPageCount) {
+                return JSValue::encode(throwException(exec, throwScope,
+                    createRangeError(exec, ASCIILiteral("'maximum' page count must be than greater than or equal to the 'initial' page count"))));
+            }
+        }
+    }
+
+    std::unique_ptr<Wasm::Memory> memory = std::make_unique<Wasm::Memory>(initialPageCount, maximumPageCount);
+
+    return JSValue::encode(JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory)));
 }
 
 static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyMemory(ExecState* state)
 {
     VM& vm = state->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(state, scope, "WebAssembly.Memory"));
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+    return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(state, throwScope, "WebAssembly.Memory"));
 }
 
 WebAssemblyMemoryConstructor* WebAssemblyMemoryConstructor::create(VM& vm, Structure* structure, WebAssemblyMemoryPrototype* thisPrototype)
index 2418ff3..803824b 100644 (file)
@@ -29,7 +29,9 @@
 #if ENABLE(WEBASSEMBLY)
 
 #include "FunctionPrototype.h"
+#include "JSArrayBuffer.h"
 #include "JSCInlines.h"
+#include "JSWebAssemblyMemory.h"
 
 #include "WebAssemblyMemoryPrototype.lut.h"
 
@@ -42,10 +44,25 @@ const ClassInfo WebAssemblyMemoryPrototype::s_info = { "WebAssembly.Memory.proto
  @end
  */
 
-WebAssemblyMemoryPrototype* WebAssemblyMemoryPrototype::create(VM& vm, JSGlobalObject*, Structure* structure)
+static EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncBuffer(ExecState*);
+
+EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncBuffer(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(exec->thisValue()); 
+    if (!memory) {
+        return JSValue::encode(throwException(exec, throwScope, 
+                    createTypeError(exec, ASCIILiteral("WebAssembly.Memory.prototype.buffer getter called with non WebAssembly.Memory |this| value"))));
+    }
+    return JSValue::encode(memory->buffer(exec->vm(), exec->lexicalGlobalObject()));
+}
+
+WebAssemblyMemoryPrototype* WebAssemblyMemoryPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
 {
     auto* object = new (NotNull, allocateCell<WebAssemblyMemoryPrototype>(vm.heap)) WebAssemblyMemoryPrototype(vm, structure);
-    object->finishCreation(vm);
+    object->finishCreation(vm, globalObject);
     return object;
 }
 
@@ -54,9 +71,10 @@ Structure* WebAssemblyMemoryPrototype::createStructure(VM& vm, JSGlobalObject* g
     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
 }
 
-void WebAssemblyMemoryPrototype::finishCreation(VM& vm)
+void WebAssemblyMemoryPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
     Base::finishCreation(vm);
+    JSC_NATIVE_GETTER("buffer", webAssemblyMemoryProtoFuncBuffer, DontEnum | Accessor);
 }
 
 WebAssemblyMemoryPrototype::WebAssemblyMemoryPrototype(VM& vm, Structure* structure)
index 4219902..f061fcd 100644 (file)
@@ -43,7 +43,7 @@ public:
     DECLARE_INFO;
 
 protected:
-    void finishCreation(VM&);
+    void finishCreation(VM&, JSGlobalObject*);
 
 private:
     WebAssemblyMemoryPrototype(VM&, Structure*);
index 51c9d79..3480a50 100644 (file)
@@ -79,7 +79,7 @@ void WebAssemblyModuleRecord::finishCreation(ExecState* exec, VM& vm, const Wasm
             break;
         }
         case Wasm::External::Memory: {
-            // FIXME https://bugs.webkit.org/show_bug.cgi?id=164134
+            // FIXME: https://bugs.webkit.org/show_bug.cgi?id=165671
             break;
         }
         case Wasm::External::Global: {
@@ -140,7 +140,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
             break;
         }
         case Wasm::External::Memory: {
-            // FIXME https://bugs.webkit.org/show_bug.cgi?id=164134
+            // FIXME: https://bugs.webkit.org/show_bug.cgi?id=165671
             break;
         }
         case Wasm::External::Global: {