WebAssembly: Implement grow_memory and current_memory
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Dec 2016 00:06:52 +0000 (00:06 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Dec 2016 00:06:52 +0000 (00:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=166448
<rdar://problem/29803676>

Reviewed by Keith Miller.

JSTests:

I rewrote some of the testWasmModuleFunctions that used Memory to use
the JS API since the jsc.cpp version can no longer use memory.

* wasm.yaml:
* wasm/function-tests/add-12.js:
(testWasmModuleFunctions):
* wasm/function-tests/br-if-loop-less-than.js:
(testWasmModuleFunctions):
* wasm/function-tests/brTableAsIf.js:
(testWasmModuleFunctions):
* wasm/function-tests/brTableManyValues.js:
(testWasmModuleFunctions):
* wasm/function-tests/brTableWithLoop.js:
(testWasmModuleFunctions):
* wasm/function-tests/dumb-eq-if-then-else.js:
* wasm/function-tests/eqz.js:
* wasm/function-tests/grow-memory-2.js: Added.
(const.func):
(assert.eq.instance.exports.foo):
* wasm/function-tests/grow-memory-3.js: Added.
* wasm/function-tests/grow-memory-4.js: Added.
(const.func):
* wasm/function-tests/grow-memory.js: Added.
(binaryShouldNotParse):
(assert.truthy):
(assert.eq):
(memory.grow):
* wasm/function-tests/i32-load.js:
(testWasmModuleFunctions):
* wasm/function-tests/i32-load8-s.js:
* wasm/function-tests/max.js:
* wasm/function-tests/min.js:
* wasm/js-api/memory-grow.js: Added.
(i.i):
(assertEq):
* wasm/js-api/test_memory.js:
* wasm/wasm.json:

Source/JavaScriptCore:

This patch implements grow_memory, current_memory, and WebAssembly.prototype.grow.
See relevant spec texts here:

https://github.com/WebAssembly/design/blob/master/Semantics.md#linear-memory-accesses
https://github.com/WebAssembly/design/blob/master/JS.md#webassemblymemoryprototypegrow

I also fix a couple miscellaneous bugs:

1. Data section now understands full init_exprs.
2. parseVarUint1 no longer has a bug where we allow values larger than 1 if
their bottom 8 bits are zero.

Since the JS API can now grow memory, we need to make calling an import
and call_indirect refresh the base memory register and the size registers.

* jsc.cpp:
(functionTestWasmModuleFunctions):
* runtime/Options.h:
* runtime/VM.h:
* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::B3IRGenerator):
(JSC::Wasm::reloadPinnedRegisters):
(JSC::Wasm::B3IRGenerator::emitReloadPinnedRegisters):
(JSC::Wasm::createJSToWasmWrapper):
(JSC::Wasm::parseAndCompile):
* wasm/WasmFormat.cpp:
(JSC::Wasm::Segment::create):
* wasm/WasmFormat.h:
(JSC::Wasm::I32InitExpr::I32InitExpr):
(JSC::Wasm::I32InitExpr::globalImport):
(JSC::Wasm::I32InitExpr::constValue):
(JSC::Wasm::I32InitExpr::isConst):
(JSC::Wasm::I32InitExpr::isGlobalImport):
(JSC::Wasm::I32InitExpr::globalImportIndex):
(JSC::Wasm::Segment::byte):
(JSC::Wasm::ModuleInformation::importFunctionCount):
(JSC::Wasm::ModuleInformation::hasMemory):
* wasm/WasmFunctionParser.h:
* wasm/WasmMemory.cpp:
(JSC::Wasm::Memory::Memory):
(JSC::Wasm::Memory::grow):
* wasm/WasmMemory.h:
(JSC::Wasm::Memory::size):
(JSC::Wasm::Memory::sizeInPages):
(JSC::Wasm::Memory::offsetOfMemory):
(JSC::Wasm::Memory::isValid): Deleted.
(JSC::Wasm::Memory::grow): Deleted.
* wasm/WasmModuleParser.cpp:
(JSC::Wasm::makeI32InitExpr):
* wasm/WasmModuleParser.h:
* wasm/WasmPageCount.h:
(JSC::Wasm::PageCount::bytes):
(JSC::Wasm::PageCount::pageCount):
(JSC::Wasm::PageCount::fromBytes):
(JSC::Wasm::PageCount::operator+):
* wasm/WasmParser.h:
(JSC::Wasm::Parser<SuccessType>::parseVarUInt1):
* wasm/WasmValidate.cpp:
* wasm/js/JSWebAssemblyInstance.h:
(JSC::JSWebAssemblyInstance::offsetOfMemory):
* wasm/js/JSWebAssemblyMemory.cpp:
(JSC::JSWebAssemblyMemory::~JSWebAssemblyMemory):
(JSC::JSWebAssemblyMemory::grow):
* wasm/js/JSWebAssemblyMemory.h:
(JSC::JSWebAssemblyMemory::offsetOfMemory):
* wasm/js/JSWebAssemblyModule.h:
(JSC::JSWebAssemblyModule::functionImportCount):
(JSC::JSWebAssemblyModule::jsEntrypointCalleeFromFunctionIndexSpace):
(JSC::JSWebAssemblyModule::wasmEntrypointCalleeFromFunctionIndexSpace):
(JSC::JSWebAssemblyModule::importCount): Deleted.
* wasm/js/WebAssemblyFunction.cpp:
(JSC::callWebAssemblyFunction):
* wasm/js/WebAssemblyInstanceConstructor.cpp:
(JSC::constructJSWebAssemblyInstance):
* wasm/js/WebAssemblyMemoryConstructor.cpp:
(JSC::constructJSWebAssemblyMemory):
* wasm/js/WebAssemblyMemoryPrototype.cpp:
(JSC::getMemory):
(JSC::webAssemblyMemoryProtoFuncBuffer):
(JSC::webAssemblyMemoryProtoFuncGrow):
* wasm/js/WebAssemblyModuleRecord.cpp:
(JSC::WebAssemblyModuleRecord::link):
(JSC::dataSegmentFail):
(JSC::WebAssemblyModuleRecord::evaluate):
* wasm/wasm.json:

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

45 files changed:
JSTests/ChangeLog
JSTests/wasm.yaml
JSTests/wasm/function-tests/add-12.js
JSTests/wasm/function-tests/br-if-loop-less-than.js
JSTests/wasm/function-tests/brTableAsIf.js
JSTests/wasm/function-tests/brTableManyValues.js
JSTests/wasm/function-tests/brTableWithLoop.js
JSTests/wasm/function-tests/dumb-eq-if-then-else.js
JSTests/wasm/function-tests/eqz.js
JSTests/wasm/function-tests/grow-memory-2.js [new file with mode: 0644]
JSTests/wasm/function-tests/grow-memory-3.js [new file with mode: 0644]
JSTests/wasm/function-tests/grow-memory-4.js [new file with mode: 0644]
JSTests/wasm/function-tests/grow-memory.js [new file with mode: 0644]
JSTests/wasm/function-tests/i32-load.js
JSTests/wasm/function-tests/i32-load8-s.js
JSTests/wasm/function-tests/max.js
JSTests/wasm/function-tests/min.js
JSTests/wasm/js-api/memory-grow.js [new file with mode: 0644]
JSTests/wasm/js-api/test_memory.js
JSTests/wasm/wasm.json
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/Options.h
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmFormat.cpp
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmFunctionParser.h
Source/JavaScriptCore/wasm/WasmMemory.cpp
Source/JavaScriptCore/wasm/WasmMemory.h
Source/JavaScriptCore/wasm/WasmModuleParser.cpp
Source/JavaScriptCore/wasm/WasmModuleParser.h
Source/JavaScriptCore/wasm/WasmPageCount.h
Source/JavaScriptCore/wasm/WasmParser.h
Source/JavaScriptCore/wasm/WasmValidate.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.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/WebAssemblyModuleRecord.cpp
Source/JavaScriptCore/wasm/wasm.json

index 1f9d672..7a3c344 100644 (file)
@@ -1,3 +1,49 @@
+2016-12-28  Saam Barati  <sbarati@apple.com>
+
+        WebAssembly: Implement grow_memory and current_memory
+        https://bugs.webkit.org/show_bug.cgi?id=166448
+        <rdar://problem/29803676>
+
+        Reviewed by Keith Miller.
+
+        I rewrote some of the testWasmModuleFunctions that used Memory to use
+        the JS API since the jsc.cpp version can no longer use memory.
+
+        * wasm.yaml:
+        * wasm/function-tests/add-12.js:
+        (testWasmModuleFunctions):
+        * wasm/function-tests/br-if-loop-less-than.js:
+        (testWasmModuleFunctions):
+        * wasm/function-tests/brTableAsIf.js:
+        (testWasmModuleFunctions):
+        * wasm/function-tests/brTableManyValues.js:
+        (testWasmModuleFunctions):
+        * wasm/function-tests/brTableWithLoop.js:
+        (testWasmModuleFunctions):
+        * wasm/function-tests/dumb-eq-if-then-else.js:
+        * wasm/function-tests/eqz.js:
+        * wasm/function-tests/grow-memory-2.js: Added.
+        (const.func):
+        (assert.eq.instance.exports.foo):
+        * wasm/function-tests/grow-memory-3.js: Added.
+        * wasm/function-tests/grow-memory-4.js: Added.
+        (const.func):
+        * wasm/function-tests/grow-memory.js: Added.
+        (binaryShouldNotParse):
+        (assert.truthy):
+        (assert.eq):
+        (memory.grow):
+        * wasm/function-tests/i32-load.js:
+        (testWasmModuleFunctions):
+        * wasm/function-tests/i32-load8-s.js:
+        * wasm/function-tests/max.js:
+        * wasm/function-tests/min.js:
+        * wasm/js-api/memory-grow.js: Added.
+        (i.i):
+        (assertEq):
+        * wasm/js-api/test_memory.js:
+        * wasm/wasm.json:
+
 2016-12-25  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         Propagate the source origin as much as possible
index db9a1dd..6bec0dc 100644 (file)
   cmd: runWebAssemblySpecTest :normal
 
 - path: wasm/spec-tests/memory_trap.wast.js
-  cmd: runWebAssemblySpecTest :skip
+  cmd: runWebAssemblySpecTest :normal
 
 - path: wasm/spec-tests/names.wast.js
   cmd: runWebAssemblySpecTest :skip
index 5d6186c..67ac1d7 100644 (file)
@@ -1,60 +1,72 @@
 import Builder from '../Builder.js'
+import * as assert from '../assert.js'
 
 const b = new Builder();
 b.Type().End()
     .Function().End()
+    .Export().Function("f0").Function("f1").End()
     .Code()
-    .Function({ params: ["i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32"], ret: "i32" }, [])
-    .GetLocal(0)
-    .GetLocal(1)
-    .I32Add()
-    .GetLocal(2)
-    .I32Add()
-    .GetLocal(3)
-    .I32Add()
-    .GetLocal(4)
-    .I32Add()
-    .GetLocal(5)
-    .I32Add()
-    .GetLocal(6)
-    .I32Add()
-    .GetLocal(7)
-    .I32Add()
-    .GetLocal(8)
-    .I32Add()
-    .GetLocal(9)
-    .I32Add()
-    .GetLocal(10)
-    .I32Add()
-    .GetLocal(11)
-    .I32Add()
-    .Return()
+        .Function("f0", { params: ["i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32"], ret: "i32" })
+            .GetLocal(0)
+            .GetLocal(1)
+            .I32Add()
+            .GetLocal(2)
+            .I32Add()
+            .GetLocal(3)
+            .I32Add()
+            .GetLocal(4)
+            .I32Add()
+            .GetLocal(5)
+            .I32Add()
+            .GetLocal(6)
+            .I32Add()
+            .GetLocal(7)
+            .I32Add()
+            .GetLocal(8)
+            .I32Add()
+            .GetLocal(9)
+            .I32Add()
+            .GetLocal(10)
+            .I32Add()
+            .GetLocal(11)
+            .I32Add()
+            .Return()
+        .End()
+        .Function("f1", { params: ["i32"], ret: "i32" })
+            .GetLocal(0)
+            .GetLocal(0)
+            .GetLocal(0)
+            .GetLocal(0)
+            .GetLocal(0)
+            .GetLocal(0)
+            .GetLocal(0)
+            .GetLocal(0)
+            .GetLocal(0)
+            .GetLocal(0)
+            .GetLocal(0)
+            .GetLocal(0)
+            .Call(0)
+            .Return()
+        .End()
     .End()
 
-    .Function({ params: ["i32"], ret: "i32" })
-    .GetLocal(0)
-    .GetLocal(0)
-    .GetLocal(0)
-    .GetLocal(0)
-    .GetLocal(0)
-    .GetLocal(0)
-    .GetLocal(0)
-    .GetLocal(0)
-    .GetLocal(0)
-    .GetLocal(0)
-    .GetLocal(0)
-    .GetLocal(0)
-    .Call(0)
-    .End()
+const bin = b.WebAssembly().get();
+const instance = new WebAssembly.Instance(new WebAssembly.Module(bin));
+function testWasmModuleFunctions(...tests) {
+    for (let i = 0; i < tests.length; i++) {
+        const func = instance.exports['f' + i];
+        for (let test of tests[i]) {
+            let result = test[0].value;
+            let args = test[1].map(x => x.value);
+            assert.eq(result, func(...args));
+        }
+    }
+}
 
-const bin = b.WebAssembly()
-bin.trim();
-testWasmModuleFunctions(bin.get(), 2,
-                        [[{ type: "i32", value: 78 }, [{ type: "i32", value: 1 }, { type: "i32", value: 2 }, { type: "i32", value: 3 }, { type: "i32", value: 4 }, { type: "i32", value: 5 }, { type: "i32", value: 6 }, { type: "i32", value: 7 }, { type: "i32", value: 8 }, { type: "i32", value: 9 }, { type: "i32", value: 10 }, { type: "i32", value: 11 }, { type: "i32", value: 12 }]],
+testWasmModuleFunctions([[{ type: "i32", value: 78 }, [{ type: "i32", value: 1 }, { type: "i32", value: 2 }, { type: "i32", value: 3 }, { type: "i32", value: 4 }, { type: "i32", value: 5 }, { type: "i32", value: 6 }, { type: "i32", value: 7 }, { type: "i32", value: 8 }, { type: "i32", value: 9 }, { type: "i32", value: 10 }, { type: "i32", value: 11 }, { type: "i32", value: 12 }]],
                          [{ type: "i32", value: 166 }, [{ type: "i32", value: 1 }, { type: "i32", value: 2 }, { type: "i32", value: 3 }, { type: "i32", value: 4 }, { type: "i32", value: 5 }, { type: "i32", value: 6 }, { type: "i32", value: 7 }, { type: "i32", value: 8 }, { type: "i32", value: 9 }, { type: "i32", value: 10 }, { type: "i32", value: 11 }, { type: "i32", value: 100 }]]
                         ],
                         [[{ type: "i32", value: 12 }, [{ type: "i32", value: 1 }]],
                          [{ type: "i32", value: 1200 }, [{ type: "i32", value: 100 }]],
                          [{ type: "i32", value: 0 }, [{ type: "i32", value: 0 }]],
-                        ]
-                       );
+                        ]);
index 28cd6fe..ee05e15 100644 (file)
@@ -1,10 +1,12 @@
 import Builder from '../Builder.js'
+import * as assert from '../assert.js'
 
 const b = new Builder();
 b.Type().End()
     .Function().End()
+    .Export().Function("f0").End()
     .Code()
-    .Function({ params: ["i32", "i32"], ret: "i32" }, [])
+    .Function("f0", { params: ["i32", "i32"], ret: "i32" })
     .Loop("void")
     .Block("void", b =>
            b.Block("void", b =>
@@ -36,10 +38,23 @@ b.Type().End()
     .End()
     .Unreachable()
     .End()
+    .End()
+
+const bin = b.WebAssembly().get();
+const instance = new WebAssembly.Instance(new WebAssembly.Module(bin));
+
+function testWasmModuleFunctions(...tests) {
+    for (let i = 0; i < tests.length; i++) {
+        const func = instance.exports['f' + i];
+        for (let test of tests[i]) {
+            let result = test[0].value;
+            let args = test[1].map(x => x.value);
+            assert.eq(result, func(...args));
+        }
+    }
+}
 
-const bin = b.WebAssembly()
-bin.trim();
-testWasmModuleFunctions(bin.get(), 1, [[{type: "i32", value: 1 }, [{ type: "i32", value: 0 }, { type: "i32", value: 1 }]],
+testWasmModuleFunctions([[{type: "i32", value: 1 }, [{ type: "i32", value: 0 }, { type: "i32", value: 1 }]],
                                        [{type: "i32", value: 0 }, [{ type: "i32", value: 1 }, { type: "i32", value: 0 }]],
                                        [{type: "i32", value: 0 }, [{ type: "i32", value: 2 }, { type: "i32", value: 1 }]],
                                        [{type: "i32", value: 1 }, [{ type: "i32", value: 1 }, { type: "i32", value: 2 }]],
index 24bf619..0e3fe76 100644 (file)
@@ -1,24 +1,37 @@
 import Builder from '../Builder.js'
+import * as assert from '../assert.js'
 
 const b = new Builder();
 b.Type().End()
     .Function().End()
+    .Export().Function('f0').End()
     .Code()
-    .Function({ params: ["i32"], ret: "i32" }, ["i32"])
-    .Block("void", (b) =>
-           b.Block("void", (b) =>
-                   b.GetLocal(0)
-                   .BrTable(1, 0)
-                   .I32Const(21)
-                   .Return()
-                  ).I32Const(20)
-           .Return()
-          ).I32Const(22)
+        .Function('f0', { params: ["i32"], ret: "i32" }, ["i32"])
+        .Block("void", (b) =>
+               b.Block("void", (b) =>
+                       b.GetLocal(0)
+                       .BrTable(1, 0)
+                       .I32Const(21)
+                       .Return()
+                      ).I32Const(20)
+               .Return()
+              ).I32Const(22)
+        .End()
     .End()
 
-const bin = b.WebAssembly()
-bin.trim();
-testWasmModuleFunctions(bin.get(), 1, [[{type: "i32", value: 22 }, [{ type: "i32", value: 0 }]],
+const bin = b.WebAssembly().get();
+const instance = new WebAssembly.Instance(new WebAssembly.Module(bin));
+function testWasmModuleFunctions(...tests) {
+    for (let i = 0; i < tests.length; i++) {
+        const func = instance.exports['f' + i];
+        for (let test of tests[i]) {
+            let result = test[0].value;
+            let args = test[1].map(x => x.value);
+            assert.eq(result, func(...args));
+        }
+    }
+}
+testWasmModuleFunctions([[{type: "i32", value: 22 }, [{ type: "i32", value: 0 }]],
                                        [{type: "i32", value: 20 }, [{ type: "i32", value: 1 }]],
                                        [{type: "i32", value: 20 }, [{ type: "i32", value: 11 }]],
                                        [{type: "i32", value: 20 }, [{ type: "i32", value: -100 }]]
index d74d081..2c4ef16 100644 (file)
@@ -1,10 +1,12 @@
 import Builder from '../Builder.js'
+import * as assert from '../assert.js'
 
 const b = new Builder();
 b.Type().End()
     .Function().End()
+    .Export().Function('f0').End()
     .Code()
-    .Function({ params: ["i32"], ret: "i32" }, ["i32"])
+    .Function('f0', { params: ["i32"], ret: "i32" }, ["i32"])
     .Block("i32", (b) =>
            b.Block("i32", (b) =>
                    b.Block("i32", (b) =>
@@ -29,9 +31,21 @@ b.Type().End()
     .I32Add()
     .Return()
     .End()
-const bin = b.WebAssembly()
-bin.trim();
-testWasmModuleFunctions(bin.get(), 1, [[{type: "i32", value: 213 }, [{ type: "i32", value: 0 }]],
+    .End()
+
+const bin = b.WebAssembly().get();
+const instance = new WebAssembly.Instance(new WebAssembly.Module(bin));
+function testWasmModuleFunctions(...tests) {
+    for (let i = 0; i < tests.length; i++) {
+        const func = instance.exports['f' + i];
+        for (let test of tests[i]) {
+            let result = test[0].value;
+            let args = test[1].map(x => x.value);
+            assert.eq(result, func(...args));
+        }
+    }
+}
+testWasmModuleFunctions([[{type: "i32", value: 213 }, [{ type: "i32", value: 0 }]],
                                        [{type: "i32", value: 212 }, [{ type: "i32", value: 1 }]],
                                        [{type: "i32", value: 211 }, [{ type: "i32", value: 2 }]],
                                        [{type: "i32", value: 210 }, [{ type: "i32", value: 3 }]],
index 0bf86cb..86d1811 100644 (file)
@@ -1,10 +1,12 @@
 import Builder from '../Builder.js'
+import * as assert from '../assert.js'
 
 const b = new Builder();
 b.Type().End()
     .Function().End()
+    .Export().Function('f0').End()
     .Code()
-    .Function({ params: ["i32"], ret: "i32" }, ["i32"])
+    .Function('f0', { params: ["i32"], ret: "i32" }, ["i32"])
     .Loop("void")
     .Block("void", (b) =>
 
@@ -18,12 +20,23 @@ b.Type().End()
            .I32Sub()
            .SetLocal(0)
            .BrTable(0, 1))
-
     .End()
     .GetLocal(1)
     .End()
-const bin = b.WebAssembly()
-bin.trim();
-testWasmModuleFunctions(bin.get(), 1, [[{type: "i32", value: 1 }, [{ type: "i32", value: 0 }]],
+    .End()
+
+const bin = b.WebAssembly().get();
+const instance = new WebAssembly.Instance(new WebAssembly.Module(bin));
+function testWasmModuleFunctions(...tests) {
+    for (let i = 0; i < tests.length; i++) {
+        const func = instance.exports['f' + i];
+        for (let test of tests[i]) {
+            let result = test[0].value;
+            let args = test[1].map(x => x.value);
+            assert.eq(result, func(...args));
+        }
+    }
+}
+testWasmModuleFunctions([[{type: "i32", value: 1 }, [{ type: "i32", value: 0 }]],
                                        [{type: "i32", value: 2 }, [{ type: "i32", value: 1 }]]
                                       ]);
index f4f08ad..aaa44f2 100644 (file)
@@ -3,7 +3,6 @@ import Builder from '../Builder.js'
 const b = new Builder();
 b.Type().End()
     .Function().End()
-    .Memory().InitialMaxPages(1, 1).End()
     .Code()
     .Function({ params: ["i32", "i32"], ret: "i32" }, [])
     .GetLocal(0)
index fedc7f0..377fb8c 100644 (file)
@@ -3,7 +3,6 @@ import Builder from '../Builder.js'
 const b = new Builder();
 b.Type().End()
     .Function().End()
-    .Memory().InitialMaxPages(1, 1).End()
     .Code()
     .Function({ params: ["i32"], ret: "i32" }, [])
     .GetLocal(0)
diff --git a/JSTests/wasm/function-tests/grow-memory-2.js b/JSTests/wasm/function-tests/grow-memory-2.js
new file mode 100644 (file)
index 0000000..fba0131
--- /dev/null
@@ -0,0 +1,82 @@
+import Builder from '../Builder.js';
+import * as assert from '../assert.js';
+
+{
+    const memoryDescription = {initial: 0, maximum: 2};
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Memory("imp", "memory", memoryDescription)
+            .Function("imp", "func", {params: [], ret: "void"})
+        .End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", {params: ["i32"], ret: "i32"})
+                .Call(0)
+                .GetLocal(0)
+                .I32Load(0, 0)
+                .Return()
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const memory = new WebAssembly.Memory(memoryDescription);
+
+    const func = () => {
+        memory.grow(1);
+        (new Uint32Array(memory.buffer))[0] = 42;
+    };
+
+    const instance = new WebAssembly.Instance(module, {imp: {memory, func}});
+    assert.eq(instance.exports.foo(0), 42);
+}
+
+{
+    const memoryDescription = {initial: 0, maximum: 2};
+    const tableDescription = {initial: 1, maximum: 1, element: "anyfunc"};
+    const builder = (new Builder())
+        .Type()
+            .Func([], "void")
+        .End()
+        .Import()
+            .Memory("imp", "memory", memoryDescription)
+            .Function("imp", "func", {params: [], ret: "void"})
+            .Table("imp", "table", tableDescription)
+        .End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+            .Function("bar")
+        .End()
+        .Code()
+            .Function("foo", {params: ["i32"], ret: "i32"})
+                .I32Const(0)
+                .CallIndirect(0, 0) // call [] => void
+                .GetLocal(0)
+                .I32Load(0, 0)
+                .Return()
+            .End()
+            .Function("bar", {params: [], ret: "void"})
+                .Call(0)
+                .Return()
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const memory = new WebAssembly.Memory(memoryDescription);
+    const table = new WebAssembly.Table(tableDescription);
+
+    const func = () => {
+        memory.grow(1);
+        (new Uint32Array(memory.buffer))[0] = 0xbadbeef;
+    };
+
+    const instance = new WebAssembly.Instance(module, {imp: {memory, func, table}});
+    table.set(0, instance.exports.bar);
+    assert.eq(instance.exports.foo(0), 0xbadbeef);
+}
diff --git a/JSTests/wasm/function-tests/grow-memory-3.js b/JSTests/wasm/function-tests/grow-memory-3.js
new file mode 100644 (file)
index 0000000..d2e1145
--- /dev/null
@@ -0,0 +1,34 @@
+import Builder from '../Builder.js';
+import * as assert from '../assert.js';
+
+{
+    const memoryDescription = {initial: 0, maximum: 2};
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Memory("imp", "memory", memoryDescription)
+        .End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+            .Function("bar")
+        .End()
+        .Code()
+            .Function("foo", {params: [], ret: "void"})
+                .Unreachable()
+                .GrowMemory(0)
+            .End()
+            .Function("bar", {params: [], ret: "void"})
+                .Unreachable()
+                .CurrentMemory(0)
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin); // Just make sure it parses.
+    const memory = new WebAssembly.Memory(memoryDescription);
+    const instance = new WebAssembly.Instance(module, {imp: {memory}});
+
+    assert.throws(() => instance.exports.foo(), WebAssembly.RuntimeError, "Unreachable code should not be executed")
+    assert.throws(() => instance.exports.bar(), WebAssembly.RuntimeError, "Unreachable code should not be executed")
+}
diff --git a/JSTests/wasm/function-tests/grow-memory-4.js b/JSTests/wasm/function-tests/grow-memory-4.js
new file mode 100644 (file)
index 0000000..f26709a
--- /dev/null
@@ -0,0 +1,34 @@
+import Builder from '../Builder.js';
+import * as assert from '../assert.js';
+
+{
+    const memoryDescription = {initial: 0, maximum: 50};
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Memory("imp", "memory", memoryDescription)
+            .Function("imp", "func", {params: [], ret: "void"})
+        .End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", {params: [], ret: "i32"})
+                .Call(0)
+                .CurrentMemory(0)
+                .Return()
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const memory = new WebAssembly.Memory(memoryDescription);
+
+    const func = () => {
+        memory.grow(42);
+    };
+
+    const instance = new WebAssembly.Instance(module, {imp: {memory, func}});
+    assert.eq(instance.exports.foo(), 42);
+}
diff --git a/JSTests/wasm/function-tests/grow-memory.js b/JSTests/wasm/function-tests/grow-memory.js
new file mode 100644 (file)
index 0000000..a30603a
--- /dev/null
@@ -0,0 +1,207 @@
+import Builder from '../Builder.js';
+import * as assert from '../assert.js';
+
+const pageSize = 64 * 1024;
+const maxPageCount = (2**32) / pageSize;
+
+function binaryShouldNotParse(builder, msg = "") {
+    const bin = builder.WebAssembly().get();
+    let threw = false;
+    try {
+        const module = new WebAssembly.Module(bin);
+    } catch(e) {
+        assert.truthy(e instanceof WebAssembly.CompileError);
+        if (msg)
+            assert.truthy(e.message.indexOf(msg) !== -1);
+        threw = true;
+    }
+    assert.truthy(threw);
+}
+
+{
+    // Can't grow_memory if no memory is defined.
+    const builder = (new Builder())
+        .Type().End()
+        .Function().End()
+        .Export().End()
+        .Code()
+            .Function({ret: "void", params: []})
+                .I32Const(25)
+                .GrowMemory(0)
+                .Drop()
+            .End()
+        .End();
+
+    binaryShouldNotParse(builder, "grow_memory is only valid if a memory is defined or imported");
+}
+
+{
+    // Can't current_memory if no memory is defined.
+    const builder = (new Builder())
+        .Type().End()
+        .Function().End()
+        .Export().End()
+        .Code()
+            .Function({ret: "void", params: []})
+                .I32Const(25)
+                .CurrentMemory(0)
+                .Drop()
+            .End()
+        .End();
+
+    binaryShouldNotParse(builder, "current_memory is only valid if a memory is defined or imported");
+}
+
+{
+    const builder = (new Builder())
+        .Type().End()
+        .Function().End()
+        .Memory().InitialMaxPages(1, 1).End()
+        .Export().End()
+        .Code()
+            .Function({ret: "void", params: []})
+                .I32Const(25)
+                .GrowMemory(1)
+                .Drop()
+            .End()
+        .End();
+
+    binaryShouldNotParse(builder, "reserved varUint1 for grow_memory must be zero");
+}
+
+{
+    const builder = (new Builder())
+        .Type().End()
+        .Function().End()
+        .Memory().InitialMaxPages(1, 1).End()
+        .Export().End()
+        .Code()
+            .Function({ret: "void", params: []})
+                .I32Const(25)
+                .CurrentMemory(1)
+                .Drop()
+            .End()
+        .End();
+
+    binaryShouldNotParse(builder, "reserved varUint1 for current_memory must be zero");
+}
+
+{
+    const builder = (new Builder())
+        .Type().End()
+        .Function().End()
+        .Memory().InitialMaxPages(1, 1).End()
+        .Export().End()
+        .Code()
+            .Function({ret: "void", params: []})
+                .I32Const(25)
+                .CurrentMemory(0xffffff00)
+                .Drop()
+            .End()
+        .End();
+
+    binaryShouldNotParse(builder, "can't parse reserved varUint1 for current_memory");
+}
+
+{
+    const builder = (new Builder())
+        .Type().End()
+        .Function().End()
+        .Memory().InitialMaxPages(1, 1).End()
+        .Export().End()
+        .Code()
+            .Function({ret: "void", params: []})
+                .I32Const(25)
+                .GrowMemory(0xffffff00)
+                .Drop()
+            .End()
+        .End();
+
+    binaryShouldNotParse(builder, "can't parse reserved varUint1 for grow_memory");
+}
+
+{
+    const memoryDescription = {initial: 20, maximum: 50};
+    const builder = (new Builder())
+        .Type().End()
+        .Import().Memory("imp", "memory", memoryDescription).End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", { params: ["i32"], ret: "i32"})
+                .GetLocal(0)
+                .GrowMemory(0)
+                .Return()
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const instance = new WebAssembly.Instance(module, {imp: {memory: new WebAssembly.Memory(memoryDescription)}});
+    let currentPageSize = memoryDescription.initial;
+    for (let i = 0; i < memoryDescription.maximum - memoryDescription.initial; i++) {
+        assert.eq(instance.exports.foo(1), currentPageSize);
+        ++currentPageSize;
+    }
+
+    for (let i = 0; i < 1000; i++) {
+        assert.eq(instance.exports.foo(1), -1);
+        assert.eq(instance.exports.foo(0), currentPageSize);
+    }
+}
+
+{
+    const memoryDescription = {initial: 20, maximum: 100};
+    const builder = (new Builder())
+        .Type().End()
+        .Import().Memory("imp", "memory", memoryDescription).End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", { params: [], ret: "i32"})
+                .CurrentMemory(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}});
+    let currentPageSize = memoryDescription.initial;
+    for (let i = 0; i < memoryDescription.maximum - memoryDescription.initial; i++) {
+        assert.eq(instance.exports.foo(), currentPageSize);
+        ++currentPageSize;
+        memory.grow(1);
+    }
+}
+
+{
+    const memoryDescription = {initial: 20, maximum: 100};
+    const builder = (new Builder())
+        .Type().End()
+        .Import().Memory("imp", "memory", memoryDescription).End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", { params: [], ret: "i32"})
+                .I32Const(-1)
+                .GrowMemory(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}});
+    for (let i = 0; i < 20; i++) {
+        assert.eq(instance.exports.foo(), -1);
+    }
+}
index 8324868..0c220cf 100644 (file)
@@ -1,11 +1,13 @@
 import Builder from '../Builder.js'
+import * as assert from '../assert.js'
 
 const b = new Builder();
 b.Type().End()
     .Function().End()
     .Memory().InitialMaxPages(1, 1).End()
+    .Export().Function('f0').End()
     .Code()
-    .Function({ params: ["i32", "i32"], ret: "i32" }, [])
+    .Function('f0', { params: ["i32", "i32"], ret: "i32" })
     .GetLocal(1)
     .GetLocal(0)
     .I32Store(2, 0)
@@ -13,10 +15,21 @@ b.Type().End()
     .I32Load(2, 0)
     .Return()
     .End()
+    .End();
 
-const bin = b.WebAssembly()
-bin.trim();
-testWasmModuleFunctions(bin.get(), 1, [[{type: "i32", value: 0 }, [{ type: "i32", value: 0 }, { type: "i32", value: 10 }]],
+const bin = b.WebAssembly().get();
+const instance = new WebAssembly.Instance(new WebAssembly.Module(bin));
+function testWasmModuleFunctions(...tests) {
+    for (let i = 0; i < tests.length; i++) {
+        const func = instance.exports['f' + i];
+        for (let test of tests[i]) {
+            let result = test[0].value;
+            let args = test[1].map(x => x.value);
+            assert.eq(result, func(...args));
+        }
+    }
+}
+testWasmModuleFunctions([[{type: "i32", value: 0 }, [{ type: "i32", value: 0 }, { type: "i32", value: 10 }]],
                                        [{type: "i32", value: 100 }, [{ type: "i32", value: 100 }, { type: "i32", value: 112 }]],
                                        [{type: "i32", value: 1000000 }, [{ type: "i32", value: 1000000 }, { type: "i32", value: 10 }]]
                                       ]);
index b9487fc..64b8ff8 100644 (file)
@@ -1,22 +1,24 @@
 import Builder from '../Builder.js'
+import * as assert from '../assert.js'
 
 const b = new Builder();
 b.Type().End()
     .Function().End()
     .Memory().InitialMaxPages(1, 1).End()
+    .Export().Function("foo").End()
     .Code()
-    .Function({ params: ["i32", "i32"], ret: "i32" }, [])
-    .GetLocal(1)
-    .GetLocal(0)
-    .I32Store(2, 0)
-    .GetLocal(1)
-    .I32Load8S(2, 0)
-    .Return()
+        .Function("foo", { params: ["i32", "i32"], ret: "i32" })
+            .GetLocal(1)
+            .GetLocal(0)
+            .I32Store(2, 0)
+            .GetLocal(1)
+            .I32Load8S(2, 0)
+            .Return()
+        .End()
     .End()
 
-const bin = b.WebAssembly()
-bin.trim();
-testWasmModuleFunctions(bin.get(), 1, [[{type: "i32", value: 0 }, [{ type: "i32", value: 0 }, { type: "i32", value: 10 }]],
-                                       [{type: "i32", value: 100 }, [{ type: "i32", value: 100 }, { type: "i32", value: 112 }]],
-                                       [{type: "i32", value: 0x40 }, [{ type: "i32", value: 1000000 }, { type: "i32", value: 10 }]]
-                                      ]);
+const bin = b.WebAssembly().get();
+const foo = (new WebAssembly.Instance(new WebAssembly.Module(bin))).exports.foo;
+assert.eq(foo(0, 10), 0);
+assert.eq(foo(100, 112), 100);
+assert.eq(foo(1000000, 10), 0x40);
index 3941eed..be4cf1c 100644 (file)
@@ -3,7 +3,6 @@ import Builder from '../Builder.js'
 const b = new Builder();
 b.Type().End()
     .Function().End()
-    .Memory().InitialMaxPages(1, 1).End()
     .Code()
     .Function({ params: ["f32", "f32"], ret: "f32" }, [])
     .GetLocal(0)
index be8c983..df89d95 100644 (file)
@@ -3,7 +3,6 @@ import Builder from '../Builder.js'
 const b = new Builder();
 b.Type().End()
     .Function().End()
-    .Memory().InitialMaxPages(1, 1).End()
     .Code()
     .Function({ params: ["f32", "f32"], ret: "f32" }, [])
     .GetLocal(0)
diff --git a/JSTests/wasm/js-api/memory-grow.js b/JSTests/wasm/js-api/memory-grow.js
new file mode 100644 (file)
index 0000000..c28001e
--- /dev/null
@@ -0,0 +1,51 @@
+import { eq as assertEq, throws as assertThrows } from "../assert.js";
+const pageSize = 64*1024;
+
+let buffers = [];
+for (let i = 0; i < 100; i++) {
+    const max = 5;
+    let pageCount = 1;
+    let x = new WebAssembly.Memory({initial: 1, maximum: max});
+    for (let i = 0; i < (max - 1); i++) {
+        let int8Array = new Uint8Array(x.buffer);
+
+        for (let i = 0; i < pageSize; i++) {
+            assertEq(int8Array[pageSize*(pageCount - 1) + i], 0);
+            int8Array[pageSize*(pageCount - 1) + i] = pageCount;
+        }
+
+        for (let i = 0; i < pageCount; i++) {
+            for (let j = 0; j < pageSize; j++) {
+                assertEq(int8Array[i * pageSize + j], i + 1);
+            }
+        }
+
+        let buffer = x.buffer;
+        assertEq(buffer.byteLength, pageCount * pageSize);
+        buffers.push(buffer);
+        let previousPageSize = x.grow(1);
+        assertEq(buffer.byteLength, 0);
+        assertEq(previousPageSize, pageCount);
+        ++pageCount;
+    }
+}
+
+for (let buffer of buffers) {
+    assertEq(buffer.byteLength, 0);
+}
+
+{
+    const memory = new WebAssembly.Memory({initial: 1, maximum: 5});
+    let buffer = memory.buffer;
+    assertEq(buffer.byteLength, 1*64*1024);
+    memory.grow(1);
+    assertEq(buffer.byteLength, 0);
+
+    buffer = memory.buffer;
+    assertEq(buffer.byteLength, 2*64*1024);
+
+    // This shouldn't neuter the buffer since it fails.
+    assertThrows(() => memory.grow(1000), Error, "Out of memory"); 
+    assertEq(buffer.byteLength, 2*64*1024);
+    assertEq(memory.buffer, buffer);
+}
index e0b98f3..01e98b1 100644 (file)
@@ -1,4 +1,3 @@
-// FIXME: use the assert library: https://bugs.webkit.org/show_bug.cgi?id=165684
 import Builder from '../Builder.js';
 import * as assert from '../assert.js';
 
index c51fb74..df3a637 100644 (file)
@@ -88,8 +88,8 @@
         "i64.store":           { "category": "memory",     "value":  55, "return": [],           "parameter": ["addr", "i64"],          "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
         "f32.store":           { "category": "memory",     "value":  56, "return": [],           "parameter": ["addr", "f32"],          "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
         "f64.store":           { "category": "memory",     "value":  57, "return": [],           "parameter": ["addr", "f64"],          "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
-        "current_memory":      { "category": "operation",  "value":  63, "return": ["size"],     "parameter": [],                       "immediate": [],                                                                                         "description": "query the size of memory" },
-        "grow_memory":         { "category": "operation",  "value":  64, "return": ["size"],     "parameter": ["size"],                 "immediate": [],                                                                                         "description": "grow the size of memory" },
+        "current_memory":      { "category": "operation",  "value":  63, "return": ["size"],     "parameter": [],                       "immediate": [{"name": "flags", "type": "varuint32"}],                                                                                         "description": "query the size of memory" },
+        "grow_memory":         { "category": "operation",  "value":  64, "return": ["size"],     "parameter": ["size"],                 "immediate": [{"name": "flags", "type": "varuint32"}],                                                                                         "description": "grow the size of memory" },
         "i32.add":             { "category": "arithmetic", "value": 106, "return": ["i32"],      "parameter": ["i32", "i32"],           "immediate": [], "b3op": "Add"          },
         "i32.sub":             { "category": "arithmetic", "value": 107, "return": ["i32"],      "parameter": ["i32", "i32"],           "immediate": [], "b3op": "Sub"          },
         "i32.mul":             { "category": "arithmetic", "value": 108, "return": ["i32"],      "parameter": ["i32", "i32"],           "immediate": [], "b3op": "Mul"          },
index f7af06f..ae2f491 100644 (file)
@@ -1,3 +1,97 @@
+2016-12-28  Saam Barati  <sbarati@apple.com>
+
+        WebAssembly: Implement grow_memory and current_memory
+        https://bugs.webkit.org/show_bug.cgi?id=166448
+        <rdar://problem/29803676>
+
+        Reviewed by Keith Miller.
+
+        This patch implements grow_memory, current_memory, and WebAssembly.prototype.grow.
+        See relevant spec texts here:
+        
+        https://github.com/WebAssembly/design/blob/master/Semantics.md#linear-memory-accesses
+        https://github.com/WebAssembly/design/blob/master/JS.md#webassemblymemoryprototypegrow
+        
+        I also fix a couple miscellaneous bugs:
+        
+        1. Data section now understands full init_exprs. 
+        2. parseVarUint1 no longer has a bug where we allow values larger than 1 if
+        their bottom 8 bits are zero.
+        
+        Since the JS API can now grow memory, we need to make calling an import
+        and call_indirect refresh the base memory register and the size registers.
+
+        * jsc.cpp:
+        (functionTestWasmModuleFunctions):
+        * runtime/Options.h:
+        * runtime/VM.h:
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::B3IRGenerator):
+        (JSC::Wasm::reloadPinnedRegisters):
+        (JSC::Wasm::B3IRGenerator::emitReloadPinnedRegisters):
+        (JSC::Wasm::createJSToWasmWrapper):
+        (JSC::Wasm::parseAndCompile):
+        * wasm/WasmFormat.cpp:
+        (JSC::Wasm::Segment::create):
+        * wasm/WasmFormat.h:
+        (JSC::Wasm::I32InitExpr::I32InitExpr):
+        (JSC::Wasm::I32InitExpr::globalImport):
+        (JSC::Wasm::I32InitExpr::constValue):
+        (JSC::Wasm::I32InitExpr::isConst):
+        (JSC::Wasm::I32InitExpr::isGlobalImport):
+        (JSC::Wasm::I32InitExpr::globalImportIndex):
+        (JSC::Wasm::Segment::byte):
+        (JSC::Wasm::ModuleInformation::importFunctionCount):
+        (JSC::Wasm::ModuleInformation::hasMemory):
+        * wasm/WasmFunctionParser.h:
+        * wasm/WasmMemory.cpp:
+        (JSC::Wasm::Memory::Memory):
+        (JSC::Wasm::Memory::grow):
+        * wasm/WasmMemory.h:
+        (JSC::Wasm::Memory::size):
+        (JSC::Wasm::Memory::sizeInPages):
+        (JSC::Wasm::Memory::offsetOfMemory):
+        (JSC::Wasm::Memory::isValid): Deleted.
+        (JSC::Wasm::Memory::grow): Deleted.
+        * wasm/WasmModuleParser.cpp:
+        (JSC::Wasm::makeI32InitExpr):
+        * wasm/WasmModuleParser.h:
+        * wasm/WasmPageCount.h:
+        (JSC::Wasm::PageCount::bytes):
+        (JSC::Wasm::PageCount::pageCount):
+        (JSC::Wasm::PageCount::fromBytes):
+        (JSC::Wasm::PageCount::operator+):
+        * wasm/WasmParser.h:
+        (JSC::Wasm::Parser<SuccessType>::parseVarUInt1):
+        * wasm/WasmValidate.cpp:
+        * wasm/js/JSWebAssemblyInstance.h:
+        (JSC::JSWebAssemblyInstance::offsetOfMemory):
+        * wasm/js/JSWebAssemblyMemory.cpp:
+        (JSC::JSWebAssemblyMemory::~JSWebAssemblyMemory):
+        (JSC::JSWebAssemblyMemory::grow):
+        * wasm/js/JSWebAssemblyMemory.h:
+        (JSC::JSWebAssemblyMemory::offsetOfMemory):
+        * wasm/js/JSWebAssemblyModule.h:
+        (JSC::JSWebAssemblyModule::functionImportCount):
+        (JSC::JSWebAssemblyModule::jsEntrypointCalleeFromFunctionIndexSpace):
+        (JSC::JSWebAssemblyModule::wasmEntrypointCalleeFromFunctionIndexSpace):
+        (JSC::JSWebAssemblyModule::importCount): Deleted.
+        * wasm/js/WebAssemblyFunction.cpp:
+        (JSC::callWebAssemblyFunction):
+        * wasm/js/WebAssemblyInstanceConstructor.cpp:
+        (JSC::constructJSWebAssemblyInstance):
+        * wasm/js/WebAssemblyMemoryConstructor.cpp:
+        (JSC::constructJSWebAssemblyMemory):
+        * wasm/js/WebAssemblyMemoryPrototype.cpp:
+        (JSC::getMemory):
+        (JSC::webAssemblyMemoryProtoFuncBuffer):
+        (JSC::webAssemblyMemoryProtoFuncGrow):
+        * wasm/js/WebAssemblyModuleRecord.cpp:
+        (JSC::WebAssemblyModuleRecord::link):
+        (JSC::dataSegmentFail):
+        (JSC::WebAssemblyModuleRecord::evaluate):
+        * wasm/wasm.json:
+
 2016-12-26  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         Use variadic templates in JSC Parser to clean up
index 65765f4..a86966b 100644 (file)
@@ -2654,8 +2654,9 @@ static EncodedJSValue JSC_HOST_CALL functionTestWasmModuleFunctions(ExecState* e
     std::unique_ptr<Wasm::ModuleInformation> moduleInformation = plan.takeModuleInformation();
 
     if (!!moduleInformation->memory) {
-        memory = std::make_unique<Wasm::Memory>(moduleInformation->memory.initial(), moduleInformation->memory.maximum());
-        RELEASE_ASSERT(memory->isValid());
+        bool failed;
+        memory = std::make_unique<Wasm::Memory>(moduleInformation->memory.initial(), moduleInformation->memory.maximum(), failed);
+        RELEASE_ASSERT(!failed);
         memoryBytes = memory->memory();
         memorySize = memory->size();
     }
index 8c3ecc3..1a46469 100644 (file)
@@ -417,6 +417,7 @@ typedef const char* optionString;
     v(bool, useCodeCache, true, Normal, "If false, the unlinked byte code cache will not be used.") \
     \
     v(bool, useWebAssembly, true, Normal, "Expose the WebAssembly global object.") \
+    v(bool, simulateWebAssemblyLowMemory, false, Normal, "If true, the Memory object won't mmap the full 'maximum' range and instead will allocate the minimum required amount.") \
 
 enum OptionEquivalence {
     SameOption,
index f340e96..0cb62c3 100644 (file)
@@ -301,8 +301,6 @@ 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 8e6b6c4..117d764 100644 (file)
@@ -172,6 +172,8 @@ public:
     // Memory
     PartialResult WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
     PartialResult WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
+    PartialResult WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result);
 
     // Basic operators
     template<OpType>
@@ -203,6 +205,8 @@ public:
 
     void emitExceptionCheck(CCallHelpers&, ExceptionType);
 
+    void emitReloadPinnedRegisters();
+
 private:
     ExpressionType emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOp);
     ExpressionType emitLoadOp(LoadOpType, Origin, ExpressionType pointer, uint32_t offset);
@@ -250,7 +254,7 @@ B3IRGenerator::B3IRGenerator(VM& vm, const ModuleInformation& info, Procedure& p
         }
     }
 
-    if (!!info.memory) {
+    if (info.hasMemory()) {
         m_memoryBaseGPR = info.memory.pinnedRegisters().baseMemoryPointer;
         m_proc.pinRegister(m_memoryBaseGPR);
         ASSERT(!info.memory.pinnedRegisters().sizeRegisters[0].sizeOffset);
@@ -334,6 +338,96 @@ auto B3IRGenerator::addUnreachable() -> PartialResult
     return { };
 }
 
+static void reloadPinnedRegisters(Procedure& proc, BasicBlock* block, const ModuleInformation& info, Value* instance)
+{
+    if (!info.hasMemory())
+        return;
+
+    const MemoryInformation* memory = &info.memory;
+
+    RegisterSet clobbers;
+    clobbers.set(memory->pinnedRegisters().baseMemoryPointer);
+    for (auto info : memory->pinnedRegisters().sizeRegisters)
+        clobbers.set(info.sizeRegister);
+
+    B3::PatchpointValue* patchpoint = block->appendNew<B3::PatchpointValue>(proc, B3::Void, Origin());
+    patchpoint->effects = Effects::none();
+    patchpoint->effects.writesPinned = true;
+    patchpoint->clobber(clobbers);
+    patchpoint->numGPScratchRegisters = 1;
+
+    patchpoint->append(instance, ValueRep::SomeRegister);
+
+    patchpoint->setGenerator([memory] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
+        AllowMacroScratchRegisterUsage allowScratch(jit);
+
+        GPRReg scratch = params.gpScratch(0);
+        jit.loadPtr(CCallHelpers::Address(params[0].gpr(), JSWebAssemblyInstance::offsetOfMemory()), scratch);
+        jit.loadPtr(CCallHelpers::Address(scratch, JSWebAssemblyMemory::offsetOfMemory()), scratch);
+        jit.loadPtr(CCallHelpers::Address(scratch, Memory::offsetOfMemory()), memory->pinnedRegisters().baseMemoryPointer);
+
+        jit.load64(CCallHelpers::Address(scratch, Memory::offsetOfSize()), scratch);
+        for (unsigned i = 0; i < memory->pinnedRegisters().sizeRegisters.size(); i++) {
+            GPRReg sizeReg = memory->pinnedRegisters().sizeRegisters[i].sizeRegister;
+            jit.move(scratch, sizeReg);
+            jit.sub64(CCallHelpers::TrustedImm32(memory->pinnedRegisters().sizeRegisters[i].sizeOffset), sizeReg);
+        }
+    });
+}
+
+void B3IRGenerator::emitReloadPinnedRegisters()
+{
+    reloadPinnedRegisters(m_proc, m_currentBlock, m_info, m_instanceValue);
+}
+
+
+auto B3IRGenerator::addGrowMemory(ExpressionType delta, ExpressionType& result) -> PartialResult
+{
+    int32_t (*growMemory) (ExecState*, int32_t) = [] (ExecState* exec, int32_t delta) -> int32_t {
+        VM& vm = exec->vm();
+        auto scope = DECLARE_THROW_SCOPE(vm);
+
+        JSWebAssemblyInstance* instance = vm.topJSWebAssemblyInstance;
+        JSWebAssemblyMemory* wasmMemory = instance->memory();
+        RELEASE_ASSERT(wasmMemory); // This would fail validation otherwise.
+
+        if (delta < 0)
+            return -1;
+
+        bool shouldThrowExceptionsOnFailure = false;
+        PageCount result = wasmMemory->grow(exec, static_cast<uint32_t>(delta), shouldThrowExceptionsOnFailure);
+        RELEASE_ASSERT(!scope.exception());
+        if (!result)
+            return -1;
+
+        return result.pageCount();
+    };
+
+    result = m_currentBlock->appendNew<CCallValue>(m_proc, Int32, Origin(),
+        m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), bitwise_cast<void*>(growMemory)),
+        m_currentBlock->appendNew<B3::Value>(m_proc, B3::FramePointer, Origin()), delta);
+
+    emitReloadPinnedRegisters();
+
+    return { };
+}
+
+auto B3IRGenerator::addCurrentMemory(ExpressionType& result) -> PartialResult
+{
+    Value* jsMemory = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), m_instanceValue, JSWebAssemblyInstance::offsetOfMemory());
+    Value* wasmMemory = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), jsMemory, JSWebAssemblyMemory::offsetOfMemory());
+    Value* size = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, B3::Int64, Origin(), wasmMemory, Memory::offsetOfSize());
+
+    constexpr uint32_t shiftValue = 16;
+    static_assert(PageCount::pageSize == 1 << shiftValue, "This must hold for the code below to be correct.");
+    Value* numPages = m_currentBlock->appendNew<Value>(m_proc, ZShr, Origin(),
+        size, m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), shiftValue));
+
+    result = m_currentBlock->appendNew<Value>(m_proc, Trunc, Origin(), numPages);
+
+    return { };
+}
+
 auto B3IRGenerator::setLocal(uint32_t index, ExpressionType value) -> PartialResult
 {
     ASSERT(m_locals[index]);
@@ -755,6 +849,10 @@ auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature* signature,
                 });
             });
         });
+
+    if (functionIndex < m_info.importFunctionCount())
+        emitReloadPinnedRegisters();
+
     return { };
 }
 
@@ -831,6 +929,8 @@ auto B3IRGenerator::addCallIndirect(const Signature* signature, SignatureIndex s
             });
         });
 
+    emitReloadPinnedRegisters();
+
     return { };
 }
 
@@ -871,7 +971,7 @@ void B3IRGenerator::dump(const Vector<ControlEntry>& controlStack, const Express
     dataLogLn();
 }
 
-static void createJSToWasmWrapper(VM& vm, CompilationContext& compilationContext, WasmInternalFunction& function, const Signature* signature, const MemoryInformation& memory)
+static void createJSToWasmWrapper(VM& vm, CompilationContext& compilationContext, WasmInternalFunction& function, const Signature* signature, const ModuleInformation& info)
 {
     Procedure proc;
     BasicBlock* block = proc.addBlock();
@@ -899,18 +999,10 @@ static void createJSToWasmWrapper(VM& vm, CompilationContext& compilationContext
     }
 
     // Move memory values to the approriate places, if needed.
-    Value* baseMemory = nullptr;
-    Vector<Value*> sizes;
-    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(), &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)));
-        }
+    {
+        Value* instance = block->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(),
+            block->appendNew<ConstPtrValue>(proc, Origin(), &vm.topJSWebAssemblyInstance));
+        reloadPinnedRegisters(proc, block, info, instance);
     }
 
     // Get our arguments.
@@ -921,13 +1013,6 @@ static void createJSToWasmWrapper(VM& vm, CompilationContext& compilationContext
 
     // 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)));
-            for (unsigned i = 0; i < sizes.size(); ++i)
-                patchpoint->append(ConstrainedValue(sizes[i], ValueRep::reg(memory.pinnedRegisters().sizeRegisters[i].sizeRegister)));
-        }
-
         CompilationContext* context = &compilationContext;
         patchpoint->setGenerator([context] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
             AllowMacroScratchRegisterUsage allowScratch(jit);
@@ -989,7 +1074,7 @@ Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM& vm,
         result->wasmEntrypoint.calleeSaveRegisters = procedure.calleeSaveRegisters();
     }
 
-    createJSToWasmWrapper(vm, compilationContext, *result, signature, info.memory);
+    createJSToWasmWrapper(vm, compilationContext, *result, signature, info);
     return WTFMove(result);
 }
 
index 40edc52..b047875 100644 (file)
@@ -34,7 +34,7 @@
 
 namespace JSC { namespace Wasm {
 
-Segment* Segment::create(uint32_t offset, uint32_t sizeInBytes)
+Segment* Segment::create(I32InitExpr offset, uint32_t sizeInBytes)
 {
     auto allocated = tryFastCalloc(sizeof(Segment) + sizeInBytes, 1);
     Segment* segment;
index 359ddcc..cb2be55 100644 (file)
@@ -138,16 +138,51 @@ struct FunctionLocationInBinary {
     size_t end;
 };
 
+class I32InitExpr {
+    enum Type : uint8_t {
+        Global,
+        Const
+    };
+
+    I32InitExpr(Type type, uint32_t bits)
+        : m_bits(bits)
+        , m_type(type)
+    { }
+
+public:
+    I32InitExpr() = delete;
+
+    static I32InitExpr globalImport(uint32_t globalImportNumber) { return I32InitExpr(Global, globalImportNumber); }
+    static I32InitExpr constValue(uint32_t constValue) { return I32InitExpr(Const, constValue); }
+
+    bool isConst() const { return m_type == Const; }
+    bool isGlobalImport() const { return m_type == Global; }
+    uint32_t constValue() const
+    {
+        RELEASE_ASSERT(isConst());
+        return m_bits;
+    }
+    uint32_t globalImportIndex() const
+    {
+        RELEASE_ASSERT(isGlobalImport());
+        return m_bits;
+    }
+
+private:
+    uint32_t m_bits;
+    Type m_type;
+};
+
 struct Segment {
-    uint32_t offset;
     uint32_t sizeInBytes;
+    I32InitExpr offset;
     // Bytes are allocated at the end.
     uint8_t& byte(uint32_t pos)
     {
         ASSERT(pos < sizeInBytes);
-        return *reinterpret_cast<uint8_t*>(reinterpret_cast<char*>(this) + sizeof(offset) + sizeof(sizeInBytes) + pos);
+        return *reinterpret_cast<uint8_t*>(reinterpret_cast<char*>(this) + sizeof(Segment) + pos);
     }
-    static Segment* create(uint32_t, uint32_t);
+    static Segment* create(I32InitExpr, uint32_t);
     static void destroy(Segment*);
     typedef std::unique_ptr<Segment, decltype(&Segment::destroy)> Ptr;
     static Ptr adoptPtr(Segment*);
@@ -191,7 +226,9 @@ struct ModuleInformation {
     Vector<Import> imports;
     Vector<SignatureIndex> importFunctionSignatureIndices;
     Vector<SignatureIndex> internalFunctionSignatureIndices;
+
     MemoryInformation memory;
+
     Vector<Export> exports;
     std::optional<uint32_t> startFunctionIndexSpace;
     Vector<Segment::Ptr> data;
@@ -200,6 +237,9 @@ struct ModuleInformation {
     Vector<Global> globals;
     unsigned firstInternalGlobal { 0 };
 
+    uint32_t importFunctionCount() const { return importFunctionSignatureIndices.size(); }
+    bool hasMemory() const { return !!memory; }
+
     ~ModuleInformation();
 };
 
index e0e49b6..52010dc 100644 (file)
@@ -478,12 +478,36 @@ auto FunctionParser<Context>::parseExpression(OpType op) -> PartialResult
         return { };
     }
 
-    case GrowMemory:
-        return fail("not yet implemented: grow_memory"); // FIXME: Not yet implemented.
+    case GrowMemory: {
+        WASM_PARSER_FAIL_IF(!m_info.memory, "grow_memory is only valid if a memory is defined or imported");
+
+        uint8_t reserved;
+        WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for grow_memory");
+        WASM_PARSER_FAIL_IF(reserved != 0, "reserved varUint1 for grow_memory must be zero");
 
-    case CurrentMemory:
-        return fail("not yet implemented: current_memory"); // FIXME: Not yet implemented.
+        ExpressionType delta;
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(delta, "expect an i32 argument to grow_memory on the stack");
 
+        ExpressionType result;
+        WASM_TRY_ADD_TO_CONTEXT(addGrowMemory(delta, result));
+        m_expressionStack.append(result);
+
+        return { };
+    }
+
+    case CurrentMemory: {
+        WASM_PARSER_FAIL_IF(!m_info.memory, "current_memory is only valid if a memory is defined or imported");
+
+        uint8_t reserved;
+        WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for current_memory");
+        WASM_PARSER_FAIL_IF(reserved != 0, "reserved varUint1 for current_memory must be zero");
+
+        ExpressionType result;
+        WASM_TRY_ADD_TO_CONTEXT(addCurrentMemory(result));
+        m_expressionStack.append(result);
+
+        return { };
+    }
     }
 
     ASSERT_NOT_REACHED();
@@ -584,6 +608,13 @@ auto FunctionParser<Context>::parseUnreachableExpression(OpType op) -> PartialRe
         return { };
     }
 
+    case GrowMemory:
+    case CurrentMemory: {
+        uint8_t reserved;
+        WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for grow_memory/current_memory");
+        return { };
+    }
+
     // no immediate cases
     FOR_EACH_WASM_BINARY_OP(CREATE_CASE)
     FOR_EACH_WASM_UNARY_OP(CREATE_CASE)
@@ -591,9 +622,7 @@ auto FunctionParser<Context>::parseUnreachableExpression(OpType op) -> PartialRe
     case Nop:
     case Return:
     case Select:
-    case Drop:
-    case GrowMemory:
-    case CurrentMemory: {
+    case Drop: {
         return { };
     }
     }
index f19ece0..700e3ac 100644 (file)
 
 namespace JSC { namespace Wasm {
 
-Memory::Memory(PageCount initial, PageCount maximum)
+static_assert(sizeof(uint64_t) == sizeof(size_t), "We rely on allowing the maximum size of Memory we map to be 2^32 which is larger than fits in a 32-bit integer that we'd pass to mprotect if this didn't hold.");
+
+Memory::Memory(PageCount initial, PageCount maximum, bool& failed)
     : m_mode(Mode::BoundsChecking)
-    , m_size(initial.bytes())
-    , m_capacity(maximum ? maximum.bytes() : PageCount::max().bytes())
     , m_initial(initial)
     , m_maximum(maximum)
+    , m_size(initial.bytes())
     // 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
 {
     RELEASE_ASSERT(!maximum || maximum >= initial); // This should be guaranteed by our caller.
 
-    m_mappedCapacity = m_capacity;
+    m_mappedCapacity = maximum ? maximum.bytes() : PageCount::max().bytes();
+    if (!m_mappedCapacity) {
+        // This means we specified a zero as maximum (which means we also have zero as initial size).
+        RELEASE_ASSERT(m_size == 0);
+        m_mappedCapacity = 0;
+        m_memory = nullptr;
+        failed = false;
+        return;
+    }
+
     // 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);
+    void* result = Options::simulateWebAssemblyLowMemory() ? MAP_FAILED : mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
     if (result == MAP_FAILED) {
         // Try again with a different number.
         m_mappedCapacity = m_size;
+        if (!m_mappedCapacity) {
+            m_memory = nullptr;
+            failed = false;
+            return;
+        }
+
         result = mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
-        if (result == MAP_FAILED)
+        if (result == MAP_FAILED) {
+            failed = true;
             return;
+        }
     }
 
     ASSERT(m_size <= m_mappedCapacity);
-    if (mprotect(result, m_size, PROT_READ | PROT_WRITE)) {
-        munmap(result, m_mappedCapacity);
-        return;
+    {
+        bool success = !mprotect(result, static_cast<size_t>(m_size), PROT_READ | PROT_WRITE);
+        RELEASE_ASSERT(success);
     }
 
     m_memory = result;
+    failed = false;
+}
+
+bool Memory::grow(PageCount newSize)
+{
+    RELEASE_ASSERT(newSize > PageCount::fromBytes(m_size));
+
+    if (maximum() && newSize > maximum())
+        return false;
+
+    uint64_t desiredSize = newSize.bytes();
+
+    if (m_memory && desiredSize <= m_mappedCapacity) {
+        bool success = !mprotect(static_cast<uint8_t*>(m_memory) + m_size, static_cast<size_t>(desiredSize - m_size), PROT_READ | PROT_WRITE);
+        RELEASE_ASSERT(success);
+        m_size = desiredSize;
+        return true;
+    }
+
+    // Otherwise, let's try to make some new memory.
+    void* newMemory = mmap(nullptr, desiredSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (newMemory == MAP_FAILED)
+        return false;
+
+    if (m_memory) {
+        memcpy(newMemory, m_memory, m_size);
+        bool success = !munmap(m_memory, m_mappedCapacity);
+        RELEASE_ASSERT(success);
+    }
+    m_memory = newMemory;
+    m_mappedCapacity = desiredSize;
+    m_size = desiredSize;
+
+    return true;
 }
 
 } // namespace JSC
index b411f34..4e5ee36 100644 (file)
@@ -44,7 +44,7 @@ public:
         BoundsChecking
     };
 
-    JS_EXPORT_PRIVATE Memory(PageCount initial, PageCount maximum);
+    JS_EXPORT_PRIVATE Memory(PageCount initial, PageCount maximum, bool& failed);
 
     ~Memory()
     {
@@ -52,35 +52,26 @@ public:
             munmap(m_memory, m_mappedCapacity);
     }
 
-    bool isValid() const { return !!m_memory; }
-
     void* memory() const { return m_memory; }
-    uint32_t size() const { return m_size; }
+    uint64_t size() const { return m_size; }
+    PageCount sizeInPages() const { return PageCount::fromBytes(m_size); }
 
     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);
-        if (newSize > m_capacity)
-            return false;
-
-        return !mprotect(m_memory, newSize, PROT_READ | PROT_WRITE);
-    }
+    bool grow(PageCount);
 
     static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(Memory, m_size); }
-
+    static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(Memory, m_memory); }
     
 private:
     void* m_memory { nullptr };
     Mode m_mode;
-    uint32_t m_size { 0 };
-    uint32_t m_capacity { 0 };
     PageCount m_initial;
     PageCount m_maximum;
+    uint64_t m_size { 0 };
     uint64_t m_mappedCapacity { 0 };
 };
 
index 8918475..ebfed86 100644 (file)
 
 namespace JSC { namespace Wasm {
 
+ALWAYS_INLINE I32InitExpr makeI32InitExpr(uint8_t opcode, uint32_t bits)
+{
+    RELEASE_ASSERT(opcode == I32Const || opcode == GetGlobal);
+    if (opcode == I32Const)
+        return I32InitExpr::constValue(bits);
+    return I32InitExpr::globalImport(bits);
+}
+
 auto ModuleParser::parse() -> Result
 {
     m_result.module = std::make_unique<ModuleInformation>();
@@ -289,7 +297,7 @@ auto ModuleParser::parseTable() -> PartialResult
 
 auto ModuleParser::parseMemoryHelper(bool isImport) -> PartialResult
 {
-    WASM_PARSER_FAIL_IF(m_result.module->memory, "Memory section cannot exist if an Import has a memory");
+    WASM_PARSER_FAIL_IF(!!m_result.module->memory, "Memory section cannot exist if an Import has a memory");
 
     PageCount initialPageCount;
     PageCount maximumPageCount;
@@ -342,32 +350,12 @@ auto ModuleParser::parseGlobal() -> PartialResult
         uint8_t initOpcode;
 
         WASM_FAIL_IF_HELPER_FAILS(parseGlobalType(global));
-        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, global.initialBitsOrImportNumber));
-
-        global.initializationType = Global::FromExpression;
         Type typeForInitOpcode;
-        switch (initOpcode) {
-        case I32Const:
-            typeForInitOpcode = I32;
-            break;
-        case I64Const:
-            typeForInitOpcode = I64;
-            break;
-        case F32Const:
-            typeForInitOpcode = F32;
-            break;
-        case F64Const:
-            typeForInitOpcode = F64;
-            break;
-        case GetGlobal:
-            WASM_PARSER_FAIL_IF(global.initialBitsOrImportNumber >= m_result.module->firstInternalGlobal, globalIndex, "th Global uses get_global ", global.initialBitsOrImportNumber, " which exceeds the first internal global ", m_result.module->firstInternalGlobal);
-            typeForInitOpcode = m_result.module->globals[global.initialBitsOrImportNumber].type;
+        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, global.initialBitsOrImportNumber, typeForInitOpcode));
+        if (initOpcode == GetGlobal)
             global.initializationType = Global::FromGlobalImport;
-            break;
-        default:
-            RELEASE_ASSERT_NOT_REACHED();
-        }
-
+        else
+            global.initializationType = Global::FromExpression;
         WASM_PARSER_FAIL_IF(typeForInitOpcode != global.type, "Global init_expr opcode of type ", typeForInitOpcode, " doesn't match global's type ", global.type);
 
         m_result.module->globals.uncheckedAppend(WTFMove(global));
@@ -451,7 +439,8 @@ auto ModuleParser::parseElement() -> PartialResult
 
         WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't get ", elementNum, "th Element table index");
         WASM_PARSER_FAIL_IF(tableIndex, "Element section can only have one Table for now");
-        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, offset));
+        Type initExprType;
+        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, offset, initExprType));
         WASM_PARSER_FAIL_IF(initOpcode != OpType::I32Const, "Element section doesn't support non-i32 init_expr opcode for now, got ", initOpcode);
         WASM_PARSER_FAIL_IF(!parseVarUInt32(indexCount), "can't get ", elementNum, "th index count for Element section");
         WASM_PARSER_FAIL_IF(indexCount == std::numeric_limits<uint32_t>::max(), "Element section's ", elementNum, "th index count is too big ", indexCount);
@@ -509,7 +498,7 @@ auto ModuleParser::parseCode() -> PartialResult
     return { };
 }
 
-auto ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber) -> PartialResult
+auto ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber, Type& resultType) -> PartialResult
 {
     WASM_PARSER_FAIL_IF(!parseUInt8(opcode), "can't get init_expr's opcode");
 
@@ -518,6 +507,7 @@ auto ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber)
         int32_t constant;
         WASM_PARSER_FAIL_IF(!parseVarInt32(constant), "can't get constant value for init_expr's i32.const");
         bitsOrImportNumber = static_cast<uint64_t>(constant);
+        resultType = I32;
         break;
     }
 
@@ -525,6 +515,7 @@ auto ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber)
         int64_t constant;
         WASM_PARSER_FAIL_IF(!parseVarInt64(constant), "can't get constant value for init_expr's i64.const");
         bitsOrImportNumber = constant;
+        resultType = I64;
         break;
     }
 
@@ -532,6 +523,7 @@ auto ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber)
         uint32_t constant;
         WASM_PARSER_FAIL_IF(!parseUInt32(constant), "can't get constant value for init_expr's f32.const");
         bitsOrImportNumber = constant;
+        resultType = F32;
         break;
     }
 
@@ -539,6 +531,7 @@ auto ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber)
         uint64_t constant;
         WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't get constant value for init_expr's f64.const");
         bitsOrImportNumber = constant;
+        resultType = F64;
         break;
     }
 
@@ -551,7 +544,7 @@ auto ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber)
         WASM_PARSER_FAIL_IF(import.kindIndex >= m_result.module->firstInternalGlobal, "get_global import kind index ", import.kindIndex, " exceeds the first internal global ", m_result.module->firstInternalGlobal);
 
         ASSERT(m_result.module->globals[import.kindIndex].mutability == Global::Immutable);
-
+        resultType = m_result.module->globals[index].type;
         bitsOrImportNumber = index;
         break;
     }
@@ -586,18 +579,19 @@ auto ModuleParser::parseData() -> PartialResult
 
     for (uint32_t segmentNumber = 0; segmentNumber < segmentCount; ++segmentNumber) {
         uint32_t index;
-        uint64_t offset;
+        uint64_t initExprBits;
         uint8_t initOpcode;
         uint32_t dataByteLength;
 
         WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get ", segmentNumber, "th Data segment's index");
         WASM_PARSER_FAIL_IF(index, segmentNumber, "th Data segment has non-zero index ", index);
-        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, offset));
-        WASM_PARSER_FAIL_IF(initOpcode != OpType::I32Const, segmentNumber, "th Data segment has opcode ", initOpcode, " expected i32.const");
+        Type initExprType;
+        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, initExprBits, initExprType));
+        WASM_PARSER_FAIL_IF(initExprType != I32, segmentNumber, "th Data segment's init_expr must produce an i32");
         WASM_PARSER_FAIL_IF(!parseVarUInt32(dataByteLength), "can't get ", segmentNumber, "th Data segment's data byte length");
         WASM_PARSER_FAIL_IF(dataByteLength == std::numeric_limits<uint32_t>::max(), segmentNumber, "th Data segment's data byte length is too big ", dataByteLength);
 
-        Segment* segment = Segment::create(offset, dataByteLength);
+        Segment* segment = Segment::create(makeI32InitExpr(initOpcode, initExprBits), dataByteLength);
         WASM_PARSER_FAIL_IF(!segment, "can't allocate enough memory for ", segmentNumber, "th Data segment of size ", dataByteLength);
         m_result.module->data.uncheckedAppend(Segment::adoptPtr(segment));
         for (uint32_t dataByte = 0; dataByte < dataByteLength; ++dataByte) {
index 96c04d4..e3b75a7 100644 (file)
@@ -64,7 +64,7 @@ private:
     PartialResult WARN_UNUSED_RETURN parseMemoryHelper(bool isImport);
     PartialResult WARN_UNUSED_RETURN parseTableHelper(bool isImport);
     PartialResult WARN_UNUSED_RETURN parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum);
-    PartialResult WARN_UNUSED_RETURN parseInitExpr(uint8_t&, uint64_t&);
+    PartialResult WARN_UNUSED_RETURN parseInitExpr(uint8_t&, uint64_t&, Type& initExprType);
 
     ModuleParserResult m_result;
     bool m_hasTable { false };
index 6a0668f..a15d505 100644 (file)
@@ -42,13 +42,22 @@ public:
         : m_pageCount(pageCount)
     { }
 
-    size_t bytes() { return m_pageCount * pageSize; }
+    uint64_t bytes() const { return static_cast<uint64_t>(m_pageCount) * static_cast<uint64_t>(pageSize); }
+    uint32_t pageCount() const { return m_pageCount; }
 
     static bool isValid(uint32_t pageCount)
     {
         return pageCount <= maxPageCount;
     }
 
+    static PageCount fromBytes(uint64_t bytes)
+    {
+        RELEASE_ASSERT(bytes % pageSize == 0);
+        uint32_t numPages = bytes / pageSize;
+        RELEASE_ASSERT(PageCount::isValid(numPages));
+        return PageCount(numPages);
+    }
+
     static PageCount max()
     {
         return PageCount(maxPageCount);
@@ -62,9 +71,18 @@ public:
     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; }
+    PageCount operator+(const PageCount& other) const
+    {
+        if (sumOverflows<uint32_t>(m_pageCount, other.m_pageCount))
+            return PageCount();
+        uint32_t newCount = m_pageCount + other.m_pageCount;
+        if (!PageCount::isValid(newCount))
+            return PageCount();
+        return PageCount(newCount);
+    }
 
-private:
     static constexpr uint32_t pageSize = 64 * KB;
+private:
     static constexpr uint32_t maxPageCount = static_cast<uint32_t>((1ull << 32) / pageSize);
 
     uint32_t m_pageCount;
index 5f154c7..6a58d88 100644 (file)
@@ -235,8 +235,10 @@ ALWAYS_INLINE bool Parser<SuccessType>::parseVarUInt1(uint8_t& result)
     uint32_t temp;
     if (!parseVarUInt32(temp))
         return false;
+    if (temp > 1)
+        return false;
     result = static_cast<uint8_t>(temp);
-    return temp <= 1;
+    return true;
 }
 
 template<typename SuccessType>
index eb1fc55..e321613 100644 (file)
@@ -132,6 +132,8 @@ public:
     Result WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack);
     Result WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
     Result WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
+    Result WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result);
+    Result WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result);
 
     Result WARN_UNUSED_RETURN addUnreachable() { return { }; }
 
@@ -292,6 +294,19 @@ auto Validate::addSwitch(ExpressionType condition, const Vector<ControlData*>& t
     return checkBranchTarget(defaultTarget, expressionStack);
 }
 
+auto Validate::addGrowMemory(ExpressionType delta, ExpressionType& result) -> Result
+{
+    WASM_VALIDATOR_FAIL_IF(delta != I32, "grow_memory with non-i32 delta");
+    result = I32;
+    return { };
+}
+
+auto Validate::addCurrentMemory(ExpressionType& result) -> Result
+{
+    result = I32;
+    return { };
+}
+
 auto Validate::endBlock(ControlEntry& entry, ExpressionList& stack) -> Result
 {
     WASM_FAIL_IF_HELPER_FAILS(unify(stack, entry.controlData));
index f34b052..928f423 100644 (file)
@@ -87,6 +87,7 @@ public:
     }
 
     static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_table); }
+    static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_memory); }
     static ptrdiff_t offsetOfGlobals() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_globals); }
 
 protected:
index bc449a8..528ab69 100644 (file)
@@ -30,8 +30,8 @@
 
 #include "JSCInlines.h"
 
-#include "JSArrayBuffer.h"
 #include "ArrayBuffer.h"
+#include "JSArrayBuffer.h"
 
 namespace JSC {
 
@@ -55,6 +55,15 @@ JSWebAssemblyMemory::JSWebAssemblyMemory(VM& vm, Structure* structure, std::uniq
 {
 }
 
+JSWebAssemblyMemory::~JSWebAssemblyMemory()
+{
+    if (m_buffer) {
+        ArrayBufferContents dummyContents;
+        m_buffer->transferTo(dummyContents);
+        m_buffer = nullptr;
+    }
+}
+
 JSArrayBuffer* JSWebAssemblyMemory::buffer(VM& vm, JSGlobalObject* globalObject)
 {
     if (m_bufferWrapper)
@@ -71,6 +80,48 @@ JSArrayBuffer* JSWebAssemblyMemory::buffer(VM& vm, JSGlobalObject* globalObject)
     return m_bufferWrapper.get();
 }
 
+Wasm::PageCount JSWebAssemblyMemory::grow(ExecState* exec, uint32_t delta, bool shouldThrowExceptionsOnFailure)
+{
+    VM& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    Wasm::PageCount oldPageCount = m_memory->sizeInPages();
+
+    if (!Wasm::PageCount::isValid(delta)) {
+        if (shouldThrowExceptionsOnFailure)
+            throwException(exec, throwScope, createRangeError(exec, ASCIILiteral("WebAssembly.Memory.grow expects the delta to be a valid page count")));
+        return Wasm::PageCount();
+    }
+
+    Wasm::PageCount newSize = oldPageCount + Wasm::PageCount(delta);
+    if (!newSize) {
+        if (shouldThrowExceptionsOnFailure)
+            throwException(exec, throwScope, createRangeError(exec, ASCIILiteral("WebAssembly.Memory.grow expects the grown size to be a valid page count")));
+        return Wasm::PageCount();
+    }
+
+    if (delta) {
+        bool success = m_memory->grow(newSize);
+        if (!success) {
+            if (shouldThrowExceptionsOnFailure)
+                throwException(exec, throwScope, createOutOfMemoryError(exec));
+            return Wasm::PageCount();
+        }
+    }
+
+    // We need to clear out the old array buffer because it might now be pointing
+    // to stale memory.
+    // Neuter the old array.
+    if (m_buffer) {
+        ArrayBufferContents dummyContents;
+        m_buffer->transferTo(dummyContents);
+        m_buffer = nullptr;
+        m_bufferWrapper.clear();
+    }
+
+    return oldPageCount;
+}
+
 void JSWebAssemblyMemory::finishCreation(VM& vm)
 {
     Base::finishCreation(vm);
index 1d43c95..28797c6 100644 (file)
@@ -48,9 +48,13 @@ public:
 
     Wasm::Memory* memory() { return m_memory.get(); }
     JSArrayBuffer* buffer(VM& vm, JSGlobalObject*);
+    Wasm::PageCount grow(ExecState*, uint32_t delta, bool shouldThrowExceptionsOnFailure);
+
+    static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(JSWebAssemblyMemory, m_memory); }
 
 protected:
     JSWebAssemblyMemory(VM&, Structure*, std::unique_ptr<Wasm::Memory>&&);
+    ~JSWebAssemblyMemory();
     void finishCreation(VM&);
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
index 2e11361..fec4eed 100644 (file)
@@ -55,20 +55,20 @@ public:
         ASSERT(functionIndexSpace < m_functionIndexSpace.size);
         return m_functionIndexSpace.buffer.get()[functionIndexSpace].signatureIndex;
     }
-    unsigned importCount() const { return m_wasmToJSStubs.size(); }
+    unsigned functionImportCount() const { return m_wasmToJSStubs.size(); }
 
     JSWebAssemblyCallee* jsEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
     {
-        RELEASE_ASSERT(functionIndexSpace >= importCount());
-        unsigned calleeIndex = functionIndexSpace - importCount();
+        RELEASE_ASSERT(functionIndexSpace >= functionImportCount());
+        unsigned calleeIndex = functionIndexSpace - functionImportCount();
         RELEASE_ASSERT(calleeIndex < m_calleeCount);
         return callees()[calleeIndex].get();
     }
 
     JSWebAssemblyCallee* wasmEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
     {
-        RELEASE_ASSERT(functionIndexSpace >= importCount());
-        unsigned calleeIndex = functionIndexSpace - importCount();
+        RELEASE_ASSERT(functionIndexSpace >= functionImportCount());
+        unsigned calleeIndex = functionIndexSpace - functionImportCount();
         RELEASE_ASSERT(calleeIndex < m_calleeCount);
         return callees()[calleeIndex + m_calleeCount].get();
     }
index 0d51e02..be27f57 100644 (file)
@@ -119,15 +119,6 @@ static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
     ProtoCallFrame protoCallFrame;
     protoCallFrame.init(nullptr, wasmFunction, firstArgument, argCount, remainingArgs);
 
-    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;
-    }
-
     JSWebAssemblyInstance* prevJSWebAssemblyInstance = vm.topJSWebAssemblyInstance;
     vm.topJSWebAssemblyInstance = wasmFunction->instance();
     ASSERT(wasmFunction->instance());
index 58ea258..d60f40e 100644 (file)
@@ -229,8 +229,9 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
         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());
-            if (!memory->isValid())
+            bool failed;
+            std::unique_ptr<Wasm::Memory> memory = std::make_unique<Wasm::Memory>(moduleInformation.memory.initial(), moduleInformation.memory.maximum(), failed);
+            if (failed)
                 return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec)));
             instance->setMemory(vm,
                JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory)));
index 3f88ed4..5d5a6cd 100644 (file)
@@ -96,8 +96,9 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyMemory(ExecState* exec
         }
     }
 
-    std::unique_ptr<Wasm::Memory> memory = std::make_unique<Wasm::Memory>(initialPageCount, maximumPageCount);
-    if (!memory->isValid())
+    bool failed;
+    std::unique_ptr<Wasm::Memory> memory = std::make_unique<Wasm::Memory>(initialPageCount, maximumPageCount, failed);
+    if (failed)
         return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec)));
 
     return JSValue::encode(JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory)));
index 803824b..f107146 100644 (file)
 #include "JSArrayBuffer.h"
 #include "JSCInlines.h"
 #include "JSWebAssemblyMemory.h"
+#include "JSWebAssemblyHelpers.h"
+
+namespace JSC {
+static EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncGrow(ExecState*);
+}
 
 #include "WebAssemblyMemoryPrototype.lut.h"
 
+
 namespace JSC {
 
 const ClassInfo WebAssemblyMemoryPrototype::s_info = { "WebAssembly.Memory.prototype", &Base::s_info, &prototypeTableWebAssemblyMemory, CREATE_METHOD_TABLE(WebAssemblyMemoryPrototype) };
 
 /* Source for WebAssemblyMemoryPrototype.lut.h
- @begin prototypeTableWebAssemblyMemory
- @end
- */
+@begin prototypeTableWebAssemblyMemory
+  grow     webAssemblyMemoryProtoFuncGrow   DontEnum|Function 1
+@end
+*/
 
-static EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncBuffer(ExecState*);
+EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncBuffer(ExecState*);
 
-EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncBuffer(ExecState* exec)
+ALWAYS_INLINE JSWebAssemblyMemory* getMemory(ExecState* exec, JSValue value)
 {
     VM& vm = exec->vm();
     auto throwScope = DECLARE_THROW_SCOPE(vm);
 
-    JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(exec->thisValue()); 
+    JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(value); 
     if (!memory) {
-        return JSValue::encode(throwException(exec, throwScope, 
-                    createTypeError(exec, ASCIILiteral("WebAssembly.Memory.prototype.buffer getter called with non WebAssembly.Memory |this| value"))));
+        throwException(exec, throwScope, 
+            createTypeError(exec, ASCIILiteral("WebAssembly.Memory.prototype.buffer getter called with non WebAssembly.Memory |this| value")));
+        return nullptr;
     }
+    return memory;
+}
+
+EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncBuffer(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    JSWebAssemblyMemory* memory = getMemory(exec, exec->thisValue()); 
+    RETURN_IF_EXCEPTION(throwScope, { });
     return JSValue::encode(memory->buffer(exec->vm(), exec->lexicalGlobalObject()));
 }
 
+EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncGrow(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    JSWebAssemblyMemory* memory = getMemory(exec, exec->thisValue()); 
+    RETURN_IF_EXCEPTION(throwScope, { });
+    
+    uint32_t delta = toNonWrappingUint32(exec, exec->argument(0));
+    RETURN_IF_EXCEPTION(throwScope, { });
+
+    bool shouldThrowExceptionsOnFailure = true;
+    Wasm::PageCount result = memory->grow(exec, delta, shouldThrowExceptionsOnFailure);
+    RETURN_IF_EXCEPTION(throwScope, { });
+
+    return JSValue::encode(jsNumber(result.pageCount()));
+}
+
 WebAssemblyMemoryPrototype* WebAssemblyMemoryPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
 {
     auto* object = new (NotNull, allocateCell<WebAssemblyMemoryPrototype>(vm.heap)) WebAssemblyMemoryPrototype(vm, structure);
index b4cb055..c1bd820 100644 (file)
@@ -95,7 +95,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
     const Wasm::ModuleInformation& moduleInformation = module->moduleInformation();
 
     SymbolTable* exportSymbolTable = module->exportSymbolTable();
-    unsigned importCount = module->importCount();
+    unsigned functionImportCount = module->functionImportCount();
 
     // FIXME wire up the imports. https://bugs.webkit.org/show_bug.cgi?id=165118
 
@@ -108,7 +108,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
             // 1. If e is a closure c:
             //   i. If there is an Exported Function Exotic Object func in funcs whose func.[[Closure]] equals c, then return func.
             //   ii. (Note: At most one wrapper is created for any closure, so func is unique, even if there are multiple occurrances in the list. Moreover, if the item was an import that is already an Exported Function Exotic Object, then the original function object will be found. For imports that are regular JS functions, a new wrapper will be created.)
-            if (exp.kindIndex < importCount) {
+            if (exp.kindIndex < functionImportCount) {
                 // FIXME Implement re-exporting an import. https://bugs.webkit.org/show_bug.cgi?id=165510
                 RELEASE_ASSERT_NOT_REACHED();
             }
@@ -180,7 +180,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
         // The start function must not take any arguments or return anything. This is enforced by the parser.
         ASSERT(!signature->argumentCount());
         ASSERT(signature->returnType() == Wasm::Void);
-        if (startFunctionIndexSpace < module->importCount()) {
+        if (startFunctionIndexSpace < module->functionImportCount()) {
             JSCell* startFunction = instance->importFunction(startFunctionIndexSpace)->get();
             m_startFunction.set(vm, this, startFunction);
         } else {
@@ -196,8 +196,8 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
     m_moduleEnvironment.set(vm, this, moduleEnvironment);
 }
 
-template <typename Scope, typename N, typename ...Args>
-NEVER_INLINE static JSValue dataSegmentFail(ExecState* state, VM& vm, Scope& scope, N memorySize, N segmentSize, N offset, Args... args)
+template <typename Scope, typename M, typename N, typename ...Args>
+NEVER_INLINE static JSValue dataSegmentFail(ExecState* state, VM& vm, Scope& scope, M memorySize, N segmentSize, N offset, Args... args)
 {
     return throwException(state, scope, createJSWebAssemblyLinkError(state, vm, makeString(ASCIILiteral("Invalid data segment initialization: segment of "), String::number(segmentSize), ASCIILiteral(" bytes memory of "), String::number(memorySize), ASCIILiteral(" bytes, at offset "), String::number(offset), args...)));
 }
@@ -231,7 +231,7 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
                 // for the import.
                 // https://bugs.webkit.org/show_bug.cgi?id=165510
                 uint32_t functionIndex = element.functionIndices[i];
-                if (functionIndex < module->importCount()) {
+                if (functionIndex < module->functionImportCount()) {
                     return JSValue::decode(
                         throwVMRangeError(state, scope, ASCIILiteral("Element is setting the table value with an import. This is not yet implemented. FIXME.")));
                 }
@@ -259,15 +259,21 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
         if (!data.isEmpty()) {
             RELEASE_ASSERT(jsMemory); // It is a validation error for a Data section to exist without a Memory section or import.
             uint8_t* memory = reinterpret_cast<uint8_t*>(jsMemory->memory()->memory());
-            RELEASE_ASSERT(memory);
-            auto sizeInBytes = jsMemory->memory()->size();
+            uint64_t sizeInBytes = jsMemory->memory()->size();
             for (auto& segment : data) {
                 if (segment->sizeInBytes) {
+                    uint32_t offset;
+                    if (segment->offset.isGlobalImport())
+                        offset = static_cast<uint32_t>(m_instance->loadI32Global(segment->offset.globalImportIndex()));
+                    else
+                        offset = segment->offset.constValue();
+
                     if (UNLIKELY(sizeInBytes < segment->sizeInBytes))
-                        return dataSegmentFail(state, vm, scope, sizeInBytes, segment->sizeInBytes, segment->offset, ASCIILiteral(", segment is too big"));
-                    if (UNLIKELY(segment->offset > sizeInBytes - segment->sizeInBytes))
-                        return dataSegmentFail(state, vm, scope, sizeInBytes, segment->sizeInBytes, segment->offset, ASCIILiteral(", segment writes outside of memory"));
-                    memcpy(memory + segment->offset, &segment->byte(0), segment->sizeInBytes);
+                        return dataSegmentFail(state, vm, scope, sizeInBytes, segment->sizeInBytes, offset, ASCIILiteral(", segment is too big"));
+                    if (UNLIKELY(offset > sizeInBytes - segment->sizeInBytes))
+                        return dataSegmentFail(state, vm, scope, sizeInBytes, segment->sizeInBytes, offset, ASCIILiteral(", segment writes outside of memory"));
+                    RELEASE_ASSERT(memory);
+                    memcpy(memory + offset, &segment->byte(0), segment->sizeInBytes);
                 }
             }
         }
index c51fb74..df3a637 100644 (file)
@@ -88,8 +88,8 @@
         "i64.store":           { "category": "memory",     "value":  55, "return": [],           "parameter": ["addr", "i64"],          "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
         "f32.store":           { "category": "memory",     "value":  56, "return": [],           "parameter": ["addr", "f32"],          "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
         "f64.store":           { "category": "memory",     "value":  57, "return": [],           "parameter": ["addr", "f64"],          "immediate": [{"name": "flags",          "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" },
-        "current_memory":      { "category": "operation",  "value":  63, "return": ["size"],     "parameter": [],                       "immediate": [],                                                                                         "description": "query the size of memory" },
-        "grow_memory":         { "category": "operation",  "value":  64, "return": ["size"],     "parameter": ["size"],                 "immediate": [],                                                                                         "description": "grow the size of memory" },
+        "current_memory":      { "category": "operation",  "value":  63, "return": ["size"],     "parameter": [],                       "immediate": [{"name": "flags", "type": "varuint32"}],                                                                                         "description": "query the size of memory" },
+        "grow_memory":         { "category": "operation",  "value":  64, "return": ["size"],     "parameter": ["size"],                 "immediate": [{"name": "flags", "type": "varuint32"}],                                                                                         "description": "grow the size of memory" },
         "i32.add":             { "category": "arithmetic", "value": 106, "return": ["i32"],      "parameter": ["i32", "i32"],           "immediate": [], "b3op": "Add"          },
         "i32.sub":             { "category": "arithmetic", "value": 107, "return": ["i32"],      "parameter": ["i32", "i32"],           "immediate": [], "b3op": "Sub"          },
         "i32.mul":             { "category": "arithmetic", "value": 108, "return": ["i32"],      "parameter": ["i32", "i32"],           "immediate": [], "b3op": "Mul"          },