We should be able to throw exceptions from Wasm code and when Wasm frames are on...
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 Dec 2016 03:11:18 +0000 (03:11 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 Dec 2016 03:11:18 +0000 (03:11 +0000)
https://bugs.webkit.org/show_bug.cgi?id=165429

Reviewed by Keith Miller.

JSTests:

* wasm/function-tests/trap-load.js: Added.
(assert):
(wasmFrameCountFromError):
(i.catch):
(assert.continuation):
* wasm/function-tests/trap-store.js: Added.
(import.Builder.from.string_appeared_here.assert):
(i.catch):
(assert.continuation):
(assert):
* wasm/js-api/test_memory_constructor.js:
(assert):

Source/JavaScriptCore:

This patch teaches the stack walking runtime about wasm.
To do this, I taught StackVisitor that a callee is not
always an object.

To be able to unwind callee save registers properly, I've given
JSWebAssemblyCallee a list of RegisterAtOffsetList for the callee
saves that B3 saved in the prologue. Also, because we have two
B3Compilations per wasm function, one for wasm entrypoint, and
one for the JS entrypoint, I needed to create a callee for each
because they each might spill callee save registers.

I also fixed a bug inside the Wasm::Memory constructor where we
were trying to mmap the same number of bytes even after the first
mmap failed. We should start by trying to mmap the maximum bytes,
and if that fails, fall back to the specified initial bytes. However,
the code was just mmapping the maximum twice. I've fixed that and
also added a RELEASE_ASSERT_NOT_REACHED() for when the second mmap
fails along with a FIXME to throw an OOM error.

There was a second bug I fixed where JSModuleRecord was calling
visitWeak on its CallLinkInfos inside ::visitChldren(). It needs
to do this after marking. I changed JSModuleRecord to do what
CodeBlock does and call visitWeak on its CallLinkInfos inside
an UnconditionalFinalizer.

* API/JSContextRef.cpp:
(BacktraceFunctor::operator()):
* inspector/ScriptCallStackFactory.cpp:
(Inspector::createScriptCallStackFromException):
* interpreter/CallFrame.cpp:
(JSC::CallFrame::vmEntryGlobalObject):
* interpreter/CallFrame.h:
(JSC::ExecState::callee):
* interpreter/Interpreter.cpp:
(JSC::GetStackTraceFunctor::operator()):
(JSC::UnwindFunctor::operator()):
(JSC::UnwindFunctor::copyCalleeSavesToVMEntryFrameCalleeSavesBuffer):
* interpreter/Interpreter.h:
* interpreter/ShadowChicken.cpp:
(JSC::ShadowChicken::update):
* interpreter/StackVisitor.cpp:
(JSC::StackVisitor::StackVisitor):
(JSC::StackVisitor::readFrame):
(JSC::StackVisitor::readNonInlinedFrame):
(JSC::StackVisitor::readInlinedFrame):
(JSC::StackVisitor::Frame::isWasmFrame):
(JSC::StackVisitor::Frame::codeType):
(JSC::StackVisitor::Frame::calleeSaveRegisters):
(JSC::StackVisitor::Frame::functionName):
(JSC::StackVisitor::Frame::sourceURL):
(JSC::StackVisitor::Frame::toString):
(JSC::StackVisitor::Frame::hasLineAndColumnInfo):
(JSC::StackVisitor::Frame::setToEnd):
* interpreter/StackVisitor.h:
(JSC::StackVisitor::Frame::callee):
(JSC::StackVisitor::Frame::isNativeFrame):
(JSC::StackVisitor::Frame::isJSFrame): Deleted.
* jsc.cpp:
(callWasmFunction):
(functionTestWasmModuleFunctions):
* runtime/Error.cpp:
(JSC::addErrorInfoAndGetBytecodeOffset):
* runtime/JSCell.cpp:
(JSC::JSCell::isAnyWasmCallee):
* runtime/JSCell.h:
* runtime/JSFunction.cpp:
(JSC::RetrieveArgumentsFunctor::operator()):
(JSC::RetrieveCallerFunctionFunctor::operator()):
* runtime/StackFrame.cpp:
(JSC::StackFrame::sourceID):
(JSC::StackFrame::sourceURL):
(JSC::StackFrame::functionName):
(JSC::StackFrame::computeLineAndColumn):
(JSC::StackFrame::toString):
* runtime/StackFrame.h:
(JSC::StackFrame::StackFrame):
(JSC::StackFrame::hasLineAndColumnInfo):
(JSC::StackFrame::hasBytecodeOffset):
(JSC::StackFrame::bytecodeOffset):
(JSC::StackFrame::isNative): Deleted.
* runtime/VM.h:
* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::B3IRGenerator):
(JSC::Wasm::createJSToWasmWrapper):
(JSC::Wasm::parseAndCompile):
* wasm/WasmCallingConvention.h:
(JSC::Wasm::CallingConvention::setupFrameInPrologue):
* wasm/WasmFormat.h:
* wasm/WasmMemory.cpp:
(JSC::Wasm::Memory::Memory):
* wasm/WasmMemory.h:
(JSC::Wasm::Memory::isValid):
* wasm/WasmPlan.cpp:
(JSC::Wasm::Plan::run):
(JSC::Wasm::Plan::initializeCallees):
* wasm/WasmPlan.h:
(JSC::Wasm::Plan::jsToWasmEntryPointForFunction): Deleted.
* wasm/js/JSWebAssemblyCallee.cpp:
(JSC::JSWebAssemblyCallee::finishCreation):
* wasm/js/JSWebAssemblyCallee.h:
(JSC::JSWebAssemblyCallee::create):
(JSC::JSWebAssemblyCallee::entrypoint):
(JSC::JSWebAssemblyCallee::calleeSaveRegisters):
(JSC::JSWebAssemblyCallee::jsToWasmEntryPoint): Deleted.
* wasm/js/JSWebAssemblyModule.cpp:
(JSC::JSWebAssemblyModule::JSWebAssemblyModule):
(JSC::JSWebAssemblyModule::visitChildren):
(JSC::JSWebAssemblyModule::UnconditionalFinalizer::finalizeUnconditionally):
* wasm/js/JSWebAssemblyModule.h:
(JSC::JSWebAssemblyModule::jsEntrypointCalleeFromFunctionIndexSpace):
(JSC::JSWebAssemblyModule::wasmEntrypointCalleeFromFunctionIndexSpace):
(JSC::JSWebAssemblyModule::setJSEntrypointCallee):
(JSC::JSWebAssemblyModule::setWasmEntrypointCallee):
(JSC::JSWebAssemblyModule::allocationSize):
(JSC::JSWebAssemblyModule::calleeFromFunctionIndexSpace): Deleted.
* wasm/js/JSWebAssemblyRuntimeError.h:
* wasm/js/WebAssemblyFunction.cpp:
(JSC::WebAssemblyFunction::call):
* wasm/js/WebAssemblyInstanceConstructor.cpp:
(JSC::constructJSWebAssemblyInstance):
* wasm/js/WebAssemblyMemoryConstructor.cpp:
(JSC::constructJSWebAssemblyMemory):
* wasm/js/WebAssemblyModuleConstructor.cpp:
(JSC::constructJSWebAssemblyModule):
* wasm/js/WebAssemblyModuleRecord.cpp:
(JSC::WebAssemblyModuleRecord::link):

Source/WebCore:

* bindings/js/JSDOMBinding.cpp:
(WebCore::GetCallerGlobalObjectFunctor::operator()):

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

41 files changed:
JSTests/ChangeLog
JSTests/wasm/function-tests/trap-load.js [new file with mode: 0644]
JSTests/wasm/function-tests/trap-store.js [new file with mode: 0644]
JSTests/wasm/js-api/test_memory_constructor.js
Source/JavaScriptCore/API/JSContextRef.cpp
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/ScriptCallStackFactory.cpp
Source/JavaScriptCore/interpreter/CallFrame.cpp
Source/JavaScriptCore/interpreter/CallFrame.h
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/interpreter/Interpreter.h
Source/JavaScriptCore/interpreter/ShadowChicken.cpp
Source/JavaScriptCore/interpreter/StackVisitor.cpp
Source/JavaScriptCore/interpreter/StackVisitor.h
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/Error.cpp
Source/JavaScriptCore/runtime/JSCell.cpp
Source/JavaScriptCore/runtime/JSCell.h
Source/JavaScriptCore/runtime/JSFunction.cpp
Source/JavaScriptCore/runtime/StackFrame.cpp
Source/JavaScriptCore/runtime/StackFrame.h
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmCallingConvention.h
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmMemory.cpp
Source/JavaScriptCore/wasm/WasmMemory.h
Source/JavaScriptCore/wasm/WasmPlan.cpp
Source/JavaScriptCore/wasm/WasmPlan.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.h
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyMemoryConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSDOMBinding.cpp

index d8bc982..58c7898 100644 (file)
@@ -1,3 +1,23 @@
+2016-12-11  Saam Barati  <sbarati@apple.com>
+
+        We should be able to throw exceptions from Wasm code and when Wasm frames are on the stack
+        https://bugs.webkit.org/show_bug.cgi?id=165429
+
+        Reviewed by Keith Miller.
+
+        * wasm/function-tests/trap-load.js: Added.
+        (assert):
+        (wasmFrameCountFromError):
+        (i.catch):
+        (assert.continuation):
+        * wasm/function-tests/trap-store.js: Added.
+        (import.Builder.from.string_appeared_here.assert):
+        (i.catch):
+        (assert.continuation):
+        (assert):
+        * wasm/js-api/test_memory_constructor.js:
+        (assert):
+
 2016-12-10  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r209653, r209654, r209663, and
diff --git a/JSTests/wasm/function-tests/trap-load.js b/JSTests/wasm/function-tests/trap-load.js
new file mode 100644 (file)
index 0000000..1a70500
--- /dev/null
@@ -0,0 +1,104 @@
+import Builder from '../Builder.js'
+
+const pageSize = 64 * 1024;
+const numPages = 10;
+
+const builder = (new Builder())
+    .Type().End()
+    .Import()
+        .Memory("a", "b", {initial: numPages})
+    .End()
+    .Function().End()
+    .Export().Function("foo").End()
+    .Code()
+        .Function("foo", {params: ["i32"], ret: "i32"})
+            .GetLocal(0)
+            .I32Load(2, 0)
+            .Return()
+        .End()
+    .End();
+
+const bin = builder.WebAssembly().get();
+const module = new WebAssembly.Module(bin);
+const foo = new WebAssembly.Instance(module, {a: {b: new WebAssembly.Memory({initial: numPages})}}).exports.foo;
+
+function assert(b) {
+    if (!b)
+        throw new Error("Bad")
+}
+
+function wasmFrameCountFromError(e) {
+    let stackFrames = e.stack.split("\n").filter((s) => s.indexOf("<wasm>@[wasm code]") !== -1);
+    return stackFrames.length;
+}
+
+for (let i = 0; i < 1000; i++) {
+    let threw = false;
+    try {
+        foo(numPages * pageSize + 1);
+    } catch(e) {
+        assert(e instanceof WebAssembly.RuntimeError);
+        assert(e.message === "Out of bounds memory access");
+        threw = true;
+        assert(wasmFrameCountFromError(e) === 2);
+    }
+    assert(threw);
+}
+
+{
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Memory("imp", "mem", {initial: numPages})
+            .Function("imp", "func", { params: ["i32"] })
+        .End()
+        .Function().End()
+        .Export().Function("foo").End()
+        .Code()
+            .Function("foo", {params: ["i32", "i32"]})
+                .GetLocal(0)
+                .I32Const(0)
+                .I32Eq()
+                .If("void", b =>
+                    b.GetLocal(1)
+                    .GetLocal(1)
+                    .I32Load(2, 0)
+                    .Br(0)
+                    .Else()
+                        .GetLocal(0)
+                        .Call(0)
+                    .Br(0)
+                   )
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const imp = {
+        imp: {
+            mem: new WebAssembly.Memory({initial: numPages}),
+            func: continuation
+        }
+    };
+    const foo = new WebAssembly.Instance(module, imp).exports.foo;
+    const address = numPages*pageSize + 1;
+    function continuation(x) {
+        foo(x - 1, address);
+    }
+
+    for (let i = 0; i < 10000; i++) {
+        let threw = false;
+        try {
+            foo(25, address);
+        } catch(e) {
+            assert(e instanceof WebAssembly.RuntimeError);
+            assert(e.message === "Out of bounds memory access");
+            // There are 25 total calls, and each call does:
+            // JS entry, wasm entry, js call stub.
+            // The last call that traps just has JS entry and wasm entry.
+            assert(wasmFrameCountFromError(e) === 25 * 3 + 2);
+            threw = true;
+        }
+        assert(threw);
+    }
+}
diff --git a/JSTests/wasm/function-tests/trap-store.js b/JSTests/wasm/function-tests/trap-store.js
new file mode 100644 (file)
index 0000000..d9f89ef
--- /dev/null
@@ -0,0 +1,97 @@
+import Builder from '../Builder.js'
+
+function assert(b) {
+    if (!b)
+        throw new Error("Bad")
+}
+
+const pageSize = 64 * 1024;
+const numPages = 10;
+
+{
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Memory("a", "b", {initial: numPages})
+        .End()
+        .Function().End()
+        .Export().Function("foo").End()
+        .Code()
+            .Function("foo", {params: ["i32", "i32"]})
+                .GetLocal(1)
+                .GetLocal(0)
+                .I32Store(2, 0)
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const foo = new WebAssembly.Instance(module, {a: {b: new WebAssembly.Memory({initial: numPages})}}).exports.foo;
+
+    for (let i = 0; i < 10000; i++) {
+        let threw = false;
+        try {
+            foo(i, numPages * pageSize + 1);
+        } catch(e) {
+            assert(e instanceof WebAssembly.RuntimeError);
+            assert(e.message === "Out of bounds memory access");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+
+{
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Memory("imp", "mem", {initial: numPages})
+            .Function("imp", "func", { params: ["i32"] })
+        .End()
+        .Function().End()
+        .Export().Function("foo").End()
+        .Code()
+            .Function("foo", {params: ["i32", "i32"]})
+                .GetLocal(0)
+                .I32Const(0)
+                .I32Eq()
+                .If("void", b =>
+                    b.GetLocal(1)
+                    .GetLocal(0)
+                    .I32Store(2, 0)
+                    .Br(0)
+                    .Else()
+                        .GetLocal(0)
+                        .Call(0)
+                    .Br(0)
+                   )
+            .End()
+        .End();
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const imp = {
+        imp: {
+            mem: new WebAssembly.Memory({initial: numPages}),
+            func: continuation
+        }
+    };
+    const foo = new WebAssembly.Instance(module, imp).exports.foo;
+    const address = numPages*pageSize + 1;
+    function continuation(x) {
+        foo(x - 1, address);
+    }
+
+    for (let i = 0; i < 10000; i++) {
+        let threw = false;
+        try {
+            foo(25, address);
+        } catch(e) {
+            assert(e instanceof WebAssembly.RuntimeError);
+            assert(e.message === "Out of bounds memory access");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
index 96c938f..e67df92 100644 (file)
@@ -39,9 +39,12 @@ function testInvalidSize(description, propName) {
         testInvalidSize({initial: v}, "initial");
     }
 
-    // These should not throw.
-    new WebAssembly.Memory({initial: maxPageCount});
-    new WebAssembly.Memory({initial: maxPageCount, maximum: maxPageCount});
+    try {
+        new WebAssembly.Memory({initial: maxPageCount});
+        new WebAssembly.Memory({initial: maxPageCount, maximum: maxPageCount});
+    } catch(e) {
+        // These might throw, since we're asking for a lot of memory.
+    }
 
     testInvalidInitial(2**31);
     testInvalidInitial(maxPageCount + 1);
index f2af448..13769df 100644 (file)
@@ -260,7 +260,7 @@ public:
         if (m_remainingCapacityForFrameCapture) {
             // If callee is unknown, but we've not added any frame yet, we should
             // still add the frame, because something called us, and gave us arguments.
-            JSObject* callee = visitor->callee();
+            JSCell* callee = visitor->callee();
             if (!callee && visitor->index())
                 return StackVisitor::Done;
 
@@ -273,7 +273,7 @@ public:
             builder.append(visitor->functionName());
             builder.appendLiteral("() at ");
             builder.append(visitor->sourceURL());
-            if (visitor->isJSFrame()) {
+            if (visitor->hasLineAndColumnInfo()) {
                 builder.append(':');
                 unsigned lineNumber;
                 unsigned unusedColumn;
index 45a74c8..48b4ebf 100644 (file)
@@ -1,3 +1,137 @@
+2016-12-11  Saam Barati  <sbarati@apple.com>
+
+        We should be able to throw exceptions from Wasm code and when Wasm frames are on the stack
+        https://bugs.webkit.org/show_bug.cgi?id=165429
+
+        Reviewed by Keith Miller.
+
+        This patch teaches the stack walking runtime about wasm.
+        To do this, I taught StackVisitor that a callee is not
+        always an object.
+
+        To be able to unwind callee save registers properly, I've given
+        JSWebAssemblyCallee a list of RegisterAtOffsetList for the callee
+        saves that B3 saved in the prologue. Also, because we have two
+        B3Compilations per wasm function, one for wasm entrypoint, and
+        one for the JS entrypoint, I needed to create a callee for each
+        because they each might spill callee save registers.
+
+        I also fixed a bug inside the Wasm::Memory constructor where we
+        were trying to mmap the same number of bytes even after the first
+        mmap failed. We should start by trying to mmap the maximum bytes,
+        and if that fails, fall back to the specified initial bytes. However,
+        the code was just mmapping the maximum twice. I've fixed that and
+        also added a RELEASE_ASSERT_NOT_REACHED() for when the second mmap
+        fails along with a FIXME to throw an OOM error.
+
+        There was a second bug I fixed where JSModuleRecord was calling
+        visitWeak on its CallLinkInfos inside ::visitChldren(). It needs
+        to do this after marking. I changed JSModuleRecord to do what
+        CodeBlock does and call visitWeak on its CallLinkInfos inside
+        an UnconditionalFinalizer.
+
+        * API/JSContextRef.cpp:
+        (BacktraceFunctor::operator()):
+        * inspector/ScriptCallStackFactory.cpp:
+        (Inspector::createScriptCallStackFromException):
+        * interpreter/CallFrame.cpp:
+        (JSC::CallFrame::vmEntryGlobalObject):
+        * interpreter/CallFrame.h:
+        (JSC::ExecState::callee):
+        * interpreter/Interpreter.cpp:
+        (JSC::GetStackTraceFunctor::operator()):
+        (JSC::UnwindFunctor::operator()):
+        (JSC::UnwindFunctor::copyCalleeSavesToVMEntryFrameCalleeSavesBuffer):
+        * interpreter/Interpreter.h:
+        * interpreter/ShadowChicken.cpp:
+        (JSC::ShadowChicken::update):
+        * interpreter/StackVisitor.cpp:
+        (JSC::StackVisitor::StackVisitor):
+        (JSC::StackVisitor::readFrame):
+        (JSC::StackVisitor::readNonInlinedFrame):
+        (JSC::StackVisitor::readInlinedFrame):
+        (JSC::StackVisitor::Frame::isWasmFrame):
+        (JSC::StackVisitor::Frame::codeType):
+        (JSC::StackVisitor::Frame::calleeSaveRegisters):
+        (JSC::StackVisitor::Frame::functionName):
+        (JSC::StackVisitor::Frame::sourceURL):
+        (JSC::StackVisitor::Frame::toString):
+        (JSC::StackVisitor::Frame::hasLineAndColumnInfo):
+        (JSC::StackVisitor::Frame::setToEnd):
+        * interpreter/StackVisitor.h:
+        (JSC::StackVisitor::Frame::callee):
+        (JSC::StackVisitor::Frame::isNativeFrame):
+        (JSC::StackVisitor::Frame::isJSFrame): Deleted.
+        * jsc.cpp:
+        (callWasmFunction):
+        (functionTestWasmModuleFunctions):
+        * runtime/Error.cpp:
+        (JSC::addErrorInfoAndGetBytecodeOffset):
+        * runtime/JSCell.cpp:
+        (JSC::JSCell::isAnyWasmCallee):
+        * runtime/JSCell.h:
+        * runtime/JSFunction.cpp:
+        (JSC::RetrieveArgumentsFunctor::operator()):
+        (JSC::RetrieveCallerFunctionFunctor::operator()):
+        * runtime/StackFrame.cpp:
+        (JSC::StackFrame::sourceID):
+        (JSC::StackFrame::sourceURL):
+        (JSC::StackFrame::functionName):
+        (JSC::StackFrame::computeLineAndColumn):
+        (JSC::StackFrame::toString):
+        * runtime/StackFrame.h:
+        (JSC::StackFrame::StackFrame):
+        (JSC::StackFrame::hasLineAndColumnInfo):
+        (JSC::StackFrame::hasBytecodeOffset):
+        (JSC::StackFrame::bytecodeOffset):
+        (JSC::StackFrame::isNative): Deleted.
+        * runtime/VM.h:
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::B3IRGenerator):
+        (JSC::Wasm::createJSToWasmWrapper):
+        (JSC::Wasm::parseAndCompile):
+        * wasm/WasmCallingConvention.h:
+        (JSC::Wasm::CallingConvention::setupFrameInPrologue):
+        * wasm/WasmFormat.h:
+        * wasm/WasmMemory.cpp:
+        (JSC::Wasm::Memory::Memory):
+        * wasm/WasmMemory.h:
+        (JSC::Wasm::Memory::isValid):
+        * wasm/WasmPlan.cpp:
+        (JSC::Wasm::Plan::run):
+        (JSC::Wasm::Plan::initializeCallees):
+        * wasm/WasmPlan.h:
+        (JSC::Wasm::Plan::jsToWasmEntryPointForFunction): Deleted.
+        * wasm/js/JSWebAssemblyCallee.cpp:
+        (JSC::JSWebAssemblyCallee::finishCreation):
+        * wasm/js/JSWebAssemblyCallee.h:
+        (JSC::JSWebAssemblyCallee::create):
+        (JSC::JSWebAssemblyCallee::entrypoint):
+        (JSC::JSWebAssemblyCallee::calleeSaveRegisters):
+        (JSC::JSWebAssemblyCallee::jsToWasmEntryPoint): Deleted.
+        * wasm/js/JSWebAssemblyModule.cpp:
+        (JSC::JSWebAssemblyModule::JSWebAssemblyModule):
+        (JSC::JSWebAssemblyModule::visitChildren):
+        (JSC::JSWebAssemblyModule::UnconditionalFinalizer::finalizeUnconditionally):
+        * wasm/js/JSWebAssemblyModule.h:
+        (JSC::JSWebAssemblyModule::jsEntrypointCalleeFromFunctionIndexSpace):
+        (JSC::JSWebAssemblyModule::wasmEntrypointCalleeFromFunctionIndexSpace):
+        (JSC::JSWebAssemblyModule::setJSEntrypointCallee):
+        (JSC::JSWebAssemblyModule::setWasmEntrypointCallee):
+        (JSC::JSWebAssemblyModule::allocationSize):
+        (JSC::JSWebAssemblyModule::calleeFromFunctionIndexSpace): Deleted.
+        * wasm/js/JSWebAssemblyRuntimeError.h:
+        * wasm/js/WebAssemblyFunction.cpp:
+        (JSC::WebAssemblyFunction::call):
+        * wasm/js/WebAssemblyInstanceConstructor.cpp:
+        (JSC::constructJSWebAssemblyInstance):
+        * wasm/js/WebAssemblyMemoryConstructor.cpp:
+        (JSC::constructJSWebAssemblyMemory):
+        * wasm/js/WebAssemblyModuleConstructor.cpp:
+        (JSC::constructJSWebAssemblyModule):
+        * wasm/js/WebAssemblyModuleRecord.cpp:
+        (JSC::WebAssemblyModuleRecord::link):
+
 2016-12-11  Filip Pizlo  <fpizlo@apple.com>
 
         Re-enable concurrent GC.
index 8913527..c4496d3 100644 (file)
@@ -156,7 +156,7 @@ Ref<ScriptCallStack> createScriptCallStackFromException(JSC::ExecState* exec, JS
             extractSourceInformationFromException(exec, exceptionObject, &lineNumber, &columnNumber, &exceptionSourceURL);
             frames.append(ScriptCallFrame(String(), exceptionSourceURL, noSourceID, lineNumber, columnNumber));
         } else {
-            if (stackTrace[0].isNative() || stackTrace[0].sourceURL().isEmpty()) {
+            if (!stackTrace[0].hasLineAndColumnInfo() || stackTrace[0].sourceURL().isEmpty()) {
                 const ScriptCallFrame& firstCallFrame = frames.first();
                 extractSourceInformationFromException(exec, exceptionObject, &lineNumber, &columnNumber, &exceptionSourceURL);
                 frames[0] = ScriptCallFrame(firstCallFrame.functionName(), exceptionSourceURL, stackTrace[0].sourceID(), lineNumber, columnNumber);
index 7637a35..7ca5b9a 100644 (file)
@@ -185,8 +185,11 @@ Register* CallFrame::topOfFrameInternal()
 
 JSGlobalObject* CallFrame::vmEntryGlobalObject()
 {
-    if (this == lexicalGlobalObject()->globalExec())
-        return lexicalGlobalObject();
+    if (callee()->isObject()) { 
+        if (this == lexicalGlobalObject()->globalExec())
+            return lexicalGlobalObject();
+    }
+    // If we're not an object, we're wasm, and therefore we're executing code and the below is safe.
 
     // For any ExecState that's not a globalExec, the 
     // dynamic global object must be set since code is running
index 9822d9c..acf83e1 100644 (file)
@@ -86,9 +86,9 @@ namespace JSC  {
     public:
         static const int headerSizeInRegisters = CallFrameSlot::argumentCount + 1;
 
-        JSCell* callee() const { return this[CallFrameSlot::callee].unboxedCell(); }
         JSValue calleeAsValue() const { return this[CallFrameSlot::callee].jsValue(); }
         JSObject* jsCallee() const { return this[CallFrameSlot::callee].object(); }
+        JSCell* callee() const { return this[CallFrameSlot::callee].unboxedCell(); }
         SUPPRESS_ASAN JSValue unsafeCallee() const { return this[CallFrameSlot::callee].asanUnsafeJSValue(); }
         CodeBlock* codeBlock() const { return this[CallFrameSlot::codeBlock].Register::codeBlock(); }
         CodeBlock** addressOfCodeBlock() const { return bitwise_cast<CodeBlock**>(this + CallFrameSlot::codeBlock); }
index 126851f..6e760f9 100644 (file)
@@ -63,6 +63,7 @@
 #include "Register.h"
 #include "ScopedArguments.h"
 #include "StackAlignment.h"
+#include "StackFrame.h"
 #include "StackVisitor.h"
 #include "StrictEvalActivation.h"
 #include "StrongInlines.h"
@@ -466,21 +467,14 @@ public:
         }
 
         if (m_remainingCapacityForFrameCapture) {
-            if (visitor->isJSFrame()
+            if (!visitor->isWasmFrame()
+                && !!visitor->codeBlock()
                 && !visitor->codeBlock()->unlinkedCodeBlock()->isBuiltinFunction()) {
-                StackFrame s = {
-                    Strong<JSObject>(m_vm, visitor->callee()),
-                    Strong<CodeBlock>(m_vm, visitor->codeBlock()),
-                    visitor->bytecodeOffset()
-                };
-                m_results.append(s);
+                m_results.append(
+                    StackFrame(m_vm, visitor->callee(), visitor->codeBlock(), visitor->bytecodeOffset()));
             } else {
-                StackFrame s = {
-                    Strong<JSObject>(m_vm, visitor->callee()),
-                    Strong<CodeBlock>(),
-                    0 // unused value because codeBlock is null.
-                };
-                m_results.append(s);
+                m_results.append(
+                    StackFrame(m_vm,  visitor->callee()));
             }
     
             m_remainingCapacityForFrameCapture--;
@@ -608,23 +602,20 @@ public:
 
         m_handler = nullptr;
         if (!m_isTermination) {
-            if (m_codeBlock)
+            if (m_codeBlock) {
                 m_handler = findExceptionHandler(visitor, m_codeBlock, RequiredHandler::AnyHandler);
+                if (m_handler)
+                    return StackVisitor::Done;
+            }
         }
 
-        if (m_handler)
-            return StackVisitor::Done;
-
         notifyDebuggerOfUnwinding(m_callFrame);
 
-        bool shouldStopUnwinding = visitor->callerIsVMEntryFrame();
-        if (shouldStopUnwinding) {
-            copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(visitor);
+        copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(visitor);
 
+        bool shouldStopUnwinding = visitor->callerIsVMEntryFrame();
+        if (shouldStopUnwinding)
             return StackVisitor::Done;
-        }
-
-        copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(visitor);
 
         return StackVisitor::Continue;
     }
@@ -633,15 +624,7 @@ private:
     void copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(StackVisitor& visitor) const
     {
 #if ENABLE(JIT) && NUMBER_OF_CALLEE_SAVES_REGISTERS > 0
-
-        if (!visitor->isJSFrame())
-            return;
-
-#if ENABLE(DFG_JIT)
-        if (visitor->inlineCallFrame())
-            return;
-#endif
-        RegisterAtOffsetList* currentCalleeSaves = m_codeBlock ? m_codeBlock->calleeSaveRegisters() : nullptr;
+        RegisterAtOffsetList* currentCalleeSaves = visitor->calleeSaveRegisters();
 
         if (!currentCalleeSaves)
             return;
index 490a610..f058bef 100644 (file)
@@ -37,7 +37,6 @@
 #include "JSObject.h"
 #include "Opcode.h"
 #include "StackAlignment.h"
-#include "StackFrame.h"
 #include <wtf/HashMap.h>
 
 #if !ENABLE(JIT)
@@ -60,6 +59,7 @@ namespace JSC {
     class ModuleProgramExecutable;
     class Register;
     class JSScope;
+    class StackFrame;
     struct CallFrameClosure;
     struct HandlerInfo;
     struct Instruction;
index 6d12771..b202824 100644 (file)
@@ -155,8 +155,16 @@ void ShadowChicken::update(VM&, ExecState* exec)
             exec, [&] (StackVisitor& visitor) -> StackVisitor::Status {
                 if (visitor->isInlinedFrame())
                     return StackVisitor::Continue;
+                if (visitor->isWasmFrame()) {
+                    // FIXME: Make shadow chicken work with Wasm.
+                    // https://bugs.webkit.org/show_bug.cgi?id=165441
+                    return StackVisitor::Continue;
+                }
+
                 bool isTailDeleted = false;
-                stackRightNow.append(Frame(visitor->callee(), visitor->callFrame(), isTailDeleted));
+                // FIXME: Make shadow chicken work with Wasm.
+                // https://bugs.webkit.org/show_bug.cgi?id=165441
+                stackRightNow.append(Frame(jsCast<JSObject*>(visitor->callee()), visitor->callFrame(), isTailDeleted));
                 return StackVisitor::Continue;
             });
         stackRightNow.reverse();
@@ -272,6 +280,11 @@ void ShadowChicken::update(VM&, ExecState* exec)
                 return StackVisitor::Continue;
             }
 
+            if (visitor->isWasmFrame()) {
+                // FIXME: Make shadow chicken work with Wasm.
+                return StackVisitor::Continue;
+            }
+
             CallFrame* callFrame = visitor->callFrame();
             if (verbose)
                 dataLog("    Examining ", RawPointer(callFrame), "\n");
@@ -293,7 +306,7 @@ void ShadowChicken::update(VM&, ExecState* exec)
                 if (scope)
                     RELEASE_ASSERT(scope->inherits(JSScope::info()));
             }
-            toPush.append(Frame(visitor->callee(), callFrame, isTailDeleted, callFrame->thisValue(), scope, codeBlock, callFrame->callSiteIndex()));
+            toPush.append(Frame(jsCast<JSObject*>(visitor->callee()), callFrame, isTailDeleted, callFrame->thisValue(), scope, codeBlock, callFrame->callSiteIndex()));
 
             if (indexInLog < logCursorIndex
                 // This condition protects us from the case where advanceIndexInLogTo didn't find
index 73f8c72..2737b3c 100644 (file)
@@ -31,6 +31,7 @@
 #include "InlineCallFrame.h"
 #include "Interpreter.h"
 #include "JSCInlines.h"
+#include "JSWebAssemblyCallee.h"
 #include <wtf/text/StringBuilder.h>
 
 namespace JSC {
@@ -38,6 +39,7 @@ namespace JSC {
 StackVisitor::StackVisitor(CallFrame* startFrame)
 {
     m_frame.m_index = 0;
+    m_frame.m_isWasmFrame = false;
     CallFrame* topFrame;
     if (startFrame) {
         m_frame.m_VMEntryFrame = startFrame->vm().topVMEntryFrame;
@@ -101,6 +103,11 @@ void StackVisitor::readFrame(CallFrame* callFrame)
         return;
     }
 
+    if (callFrame->callee()->isAnyWasmCallee()) {
+        readNonInlinedFrame(callFrame);
+        return;
+    }
+
 #if !ENABLE(DFG_JIT)
     readNonInlinedFrame(callFrame);
 
@@ -146,11 +153,23 @@ void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOri
     m_frame.m_CallerVMEntryFrame = m_frame.m_VMEntryFrame;
     m_frame.m_callerFrame = callFrame->callerFrame(m_frame.m_CallerVMEntryFrame);
     m_frame.m_callerIsVMEntryFrame = m_frame.m_CallerVMEntryFrame != m_frame.m_VMEntryFrame;
-    m_frame.m_callee = callFrame->jsCallee();
-    m_frame.m_codeBlock = callFrame->codeBlock();
-    m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0
-        : codeOrigin ? codeOrigin->bytecodeIndex
-        : callFrame->bytecodeOffset();
+    m_frame.m_isWasmFrame = false;
+
+    JSCell* callee = callFrame->callee();
+    m_frame.m_callee = callee;
+
+    if (callee->isAnyWasmCallee()) {
+        m_frame.m_isWasmFrame = true;
+        m_frame.m_codeBlock = nullptr;
+        m_frame.m_bytecodeOffset = 0;
+    } else {
+        m_frame.m_codeBlock = callFrame->codeBlock();
+        m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0
+            : codeOrigin ? codeOrigin->bytecodeIndex
+            : callFrame->bytecodeOffset();
+
+    }
+
 #if ENABLE(DFG_JIT)
     m_frame.m_inlineCallFrame = 0;
 #endif
@@ -167,6 +186,7 @@ static int inlinedFrameOffset(CodeOrigin* codeOrigin)
 void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
 {
     ASSERT(codeOrigin);
+    m_frame.m_isWasmFrame = false;
 
     int frameOffset = inlinedFrameOffset(codeOrigin);
     bool isInlined = !!frameOffset;
@@ -198,9 +218,17 @@ void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin
 }
 #endif // ENABLE(DFG_JIT)
 
+bool StackVisitor::Frame::isWasmFrame() const
+{
+    return m_isWasmFrame;
+}
+
 StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const
 {
-    if (!isJSFrame())
+    if (isWasmFrame())
+        return CodeType::Wasm;
+
+    if (!codeBlock())
         return CodeType::Native;
 
     switch (codeBlock()->codeType()) {
@@ -217,12 +245,39 @@ StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const
     return CodeType::Global;
 }
 
+RegisterAtOffsetList* StackVisitor::Frame::calleeSaveRegisters()
+{
+    if (isInlinedFrame())
+        return nullptr;
+
+#if ENABLE(WEBASSEMBLY)
+    if (isWasmFrame()) {
+        if (JSCell* callee = this->callee()) {
+            if (JSWebAssemblyCallee* wasmCallee = jsDynamicCast<JSWebAssemblyCallee*>(callee))
+                return wasmCallee->calleeSaveRegisters();
+            // Other wasm callees (e.g, stubs) don't use callee save registers, so nothing needs
+            // to be restored for them.
+        }
+
+        return nullptr;
+    }
+#endif
+
+    if (CodeBlock* codeBlock = this->codeBlock())
+        return codeBlock->calleeSaveRegisters();
+
+    return nullptr;
+}
+
 String StackVisitor::Frame::functionName() const
 {
     String traceLine;
-    JSObject* callee = this->callee();
+    JSCell* callee = this->callee();
 
     switch (codeType()) {
+    case CodeType::Wasm:
+        traceLine = ASCIILiteral("wasm code");
+        break;
     case CodeType::Eval:
         traceLine = ASCIILiteral("eval code");
         break;
@@ -231,10 +286,10 @@ String StackVisitor::Frame::functionName() const
         break;
     case CodeType::Native:
         if (callee)
-            traceLine = getCalculatedDisplayName(callFrame()->vm(), callee).impl();
+            traceLine = getCalculatedDisplayName(callFrame()->vm(), jsCast<JSObject*>(callee)).impl();
         break;
     case CodeType::Function:
-        traceLine = getCalculatedDisplayName(callFrame()->vm(), callee).impl();
+        traceLine = getCalculatedDisplayName(callFrame()->vm(), jsCast<JSObject*>(callee)).impl();
         break;
     case CodeType::Global:
         traceLine = ASCIILiteral("global code");
@@ -260,6 +315,9 @@ String StackVisitor::Frame::sourceURL() const
     case CodeType::Native:
         traceLine = ASCIILiteral("[native code]");
         break;
+    case CodeType::Wasm:
+        traceLine = ASCIILiteral("[wasm code]");
+        break;
     }
     return traceLine.isNull() ? emptyString() : traceLine;
 }
@@ -274,7 +332,7 @@ String StackVisitor::Frame::toString() const
         if (!functionName.isEmpty())
             traceBuild.append('@');
         traceBuild.append(sourceURL);
-        if (isJSFrame()) {
+        if (hasLineAndColumnInfo()) {
             unsigned line = 0;
             unsigned column = 0;
             computeLineAndColumn(line, column);
@@ -314,6 +372,11 @@ ClonedArguments* StackVisitor::Frame::createArguments()
     return arguments;
 }
 
+bool StackVisitor::Frame::hasLineAndColumnInfo() const
+{
+    return !!codeBlock();
+}
+
 void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) const
 {
     CodeBlock* codeBlock = this->codeBlock();
@@ -350,6 +413,7 @@ void StackVisitor::Frame::setToEnd()
 #if ENABLE(DFG_JIT)
     m_inlineCallFrame = 0;
 #endif
+    m_isWasmFrame = false;
 }
 
 void StackVisitor::Frame::dump(PrintStream& out, Indenter indent) const
index b63e894..0aac906 100644 (file)
@@ -41,6 +41,7 @@ class JSFunction;
 class JSObject;
 class ClonedArguments;
 class Register;
+class RegisterAtOffsetList;
 
 typedef ExecState CallFrame;
 
@@ -53,14 +54,15 @@ public:
             Eval,
             Function,
             Module,
-            Native
+            Native,
+            Wasm
         };
 
         size_t index() const { return m_index; }
         size_t argumentCountIncludingThis() const { return m_argumentCountIncludingThis; }
         bool callerIsVMEntryFrame() const { return m_callerIsVMEntryFrame; }
         CallFrame* callerFrame() const { return m_callerFrame; }
-        JSObject* callee() const { return m_callee; }
+        JSCell* callee() const { return m_callee; }
         CodeBlock* codeBlock() const { return m_codeBlock; }
         unsigned bytecodeOffset() const { return m_bytecodeOffset; }
         InlineCallFrame* inlineCallFrame() const {
@@ -71,8 +73,9 @@ public:
 #endif
         }
 
-        bool isJSFrame() const { return !!codeBlock(); }
+        bool isNativeFrame() const { return !codeBlock() && !isWasmFrame(); }
         bool isInlinedFrame() const { return !!inlineCallFrame(); }
+        bool isWasmFrame() const;
 
         JS_EXPORT_PRIVATE String functionName() const;
         JS_EXPORT_PRIVATE String sourceURL() const;
@@ -81,8 +84,11 @@ public:
         intptr_t sourceID();
 
         CodeType codeType() const;
+        bool hasLineAndColumnInfo() const;
         JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column) const;
 
+        RegisterAtOffsetList* calleeSaveRegisters();
+
         ClonedArguments* createArguments();
         VMEntryFrame* vmEntryFrame() const { return m_VMEntryFrame; }
         CallFrame* callFrame() const { return m_callFrame; }
@@ -97,19 +103,20 @@ public:
         void retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) const;
         void setToEnd();
 
-        size_t m_index;
-        size_t m_argumentCountIncludingThis;
+#if ENABLE(DFG_JIT)
+        InlineCallFrame* m_inlineCallFrame;
+#endif
+        CallFrame* m_callFrame;
         VMEntryFrame* m_VMEntryFrame;
         VMEntryFrame* m_CallerVMEntryFrame;
         CallFrame* m_callerFrame;
-        JSObject* m_callee;
+        JSCell* m_callee;
         CodeBlock* m_codeBlock;
+        size_t m_index;
+        size_t m_argumentCountIncludingThis;
         unsigned m_bytecodeOffset;
-        bool m_callerIsVMEntryFrame;
-#if ENABLE(DFG_JIT)
-        InlineCallFrame* m_inlineCallFrame;
-#endif
-        CallFrame* m_callFrame;
+        bool m_callerIsVMEntryFrame : 1;
+        bool m_isWasmFrame : 1;
 
         friend class StackVisitor;
     };
index 940425c..ec79d83 100644 (file)
@@ -2593,7 +2593,7 @@ static JSValue callWasmFunction(VM* vm, JSGlobalObject* globalObject, JSWebAssem
     ProtoCallFrame protoCallFrame;
     protoCallFrame.init(nullptr, globalObject->globalExec()->jsCallee(), firstArgument, argCount, remainingArgs);
 
-    return JSValue::decode(vmEntryToWasm(wasmCallee->jsToWasmEntryPoint(), vm, &protoCallFrame));
+    return JSValue::decode(vmEntryToWasm(wasmCallee->entrypoint(), vm, &protoCallFrame));
 }
 
 // testWasmModule(JSArrayBufferView source, number functionCount, ...[[WasmValue, [WasmValue]]]) where the ith copy of [[result, [args]]] is a list
@@ -2623,12 +2623,14 @@ static EncodedJSValue JSC_HOST_CALL functionTestWasmModuleFunctions(ExecState* e
         CRASH();
 
     MarkedArgumentBuffer callees;
+    MarkedArgumentBuffer keepAlive;
     {
         unsigned lastIndex = UINT_MAX;
         plan.initializeCallees(exec->lexicalGlobalObject(),
-            [&] (unsigned calleeIndex, JSWebAssemblyCallee* callee) {
+            [&] (unsigned calleeIndex, JSWebAssemblyCallee* jsEntrypointCallee, JSWebAssemblyCallee* wasmEntrypointCallee) {
                 RELEASE_ASSERT(!calleeIndex || (calleeIndex - 1 == lastIndex));
-                callees.append(callee);
+                callees.append(jsEntrypointCallee);
+                keepAlive.append(wasmEntrypointCallee);
                 lastIndex = calleeIndex;
             });
     }
@@ -2640,6 +2642,7 @@ static EncodedJSValue JSC_HOST_CALL functionTestWasmModuleFunctions(ExecState* e
 
     if (!!moduleInformation->memory) {
         memory = std::make_unique<Wasm::Memory>(moduleInformation->memory.initial(), moduleInformation->memory.maximum());
+        RELEASE_ASSERT(memory->isValid());
         memoryBytes = memory->memory();
         memorySize = memory->size();
     }
@@ -2657,7 +2660,12 @@ static EncodedJSValue JSC_HOST_CALL functionTestWasmModuleFunctions(ExecState* e
             for (unsigned argIndex = 0; argIndex < arguments->length(); ++argIndex)
                 boxedArgs.append(box(exec, vm, arguments->getIndexQuickly(argIndex)));
 
-            JSValue callResult = callWasmFunction(&vm, exec->lexicalGlobalObject(), jsCast<JSWebAssemblyCallee*>(callees.at(i)), boxedArgs);
+            JSValue callResult;
+            {
+                auto scope = DECLARE_THROW_SCOPE(vm);
+                callResult = callWasmFunction(&vm, exec->lexicalGlobalObject(), jsCast<JSWebAssemblyCallee*>(callees.at(i)), boxedArgs);
+                RETURN_IF_EXCEPTION(scope, { });
+            }
             JSValue expected = box(exec, vm, result);
             if (callResult != expected) {
                 dataLog("Arguments: ");
index bdc2905..677dc9b 100644 (file)
@@ -165,10 +165,10 @@ bool addErrorInfoAndGetBytecodeOffset(ExecState* exec, VM& vm, JSObject* obj, bo
 
         ASSERT(exec == vm.topCallFrame || exec == exec->lexicalGlobalObject()->globalExec() || exec == exec->vmEntryGlobalObject()->globalExec());
 
-        StackFrame* firstNonNativeFrame = nullptr;
+        StackFrame* firstFrameWithLineAndColumnInfo = nullptr;
         for (unsigned i = 0 ; i < stackTrace.size(); ++i) {
-            firstNonNativeFrame = &stackTrace.at(i);
-            if (!firstNonNativeFrame->isNative())
+            firstFrameWithLineAndColumnInfo = &stackTrace.at(i);
+            if (firstFrameWithLineAndColumnInfo->hasLineAndColumnInfo())
                 break;
         }
 
@@ -177,16 +177,18 @@ bool addErrorInfoAndGetBytecodeOffset(ExecState* exec, VM& vm, JSObject* obj, bo
             vm.topCallFrame->iterate(functor);
             callFrame = functor.foundCallFrame();
             unsigned stackIndex = functor.index();
-            *bytecodeOffset = stackTrace.at(stackIndex).bytecodeOffset;
+            *bytecodeOffset = 0;
+            if (stackTrace.at(stackIndex).hasBytecodeOffset())
+                *bytecodeOffset = stackTrace.at(stackIndex).bytecodeOffset();
         }
         
         unsigned line;
         unsigned column;
-        firstNonNativeFrame->computeLineAndColumn(line, column);
+        firstFrameWithLineAndColumnInfo->computeLineAndColumn(line, column);
         obj->putDirect(vm, vm.propertyNames->line, jsNumber(line));
         obj->putDirect(vm, vm.propertyNames->column, jsNumber(column));
 
-        String frameSourceURL = firstNonNativeFrame->sourceURL();
+        String frameSourceURL = firstFrameWithLineAndColumnInfo->sourceURL();
         if (!frameSourceURL.isEmpty())
             obj->putDirect(vm, vm.propertyNames->sourceURL, jsString(&vm, frameSourceURL));
 
index 0082c1f..4298566 100644 (file)
 #include "JSCell.h"
 
 #include "ArrayBufferView.h"
+#include "JSCInlines.h"
 #include "JSFunction.h"
 #include "JSString.h"
 #include "JSObject.h"
+#include "JSWebAssemblyCallee.h"
 #include "NumberObject.h"
-#include "JSCInlines.h"
+#include "WebAssemblyToJSCallee.h"
 #include <wtf/MathExtras.h>
 
 namespace JSC {
@@ -293,4 +295,14 @@ JSValue JSCell::getPrototype(JSObject*, ExecState*)
     RELEASE_ASSERT_NOT_REACHED();
 }
 
+bool JSCell::isAnyWasmCallee() const
+{
+#if ENABLE(WEBASSEMBLY)
+    return inherits(JSWebAssemblyCallee::info()) || inherits(WebAssemblyToJSCallee::info());
+#else
+    return false;
+#endif
+
+}
+
 } // namespace JSC
index ed2a70d..e63feaf 100644 (file)
@@ -95,6 +95,7 @@ public:
     bool isString() const;
     bool isSymbol() const;
     bool isObject() const;
+    bool isAnyWasmCallee() const;
     bool isGetterSetter() const;
     bool isCustomGetterSetter() const;
     bool isProxy() const;
index d074f22..567acc8 100644 (file)
@@ -235,7 +235,7 @@ public:
 
     StackVisitor::Status operator()(StackVisitor& visitor) const
     {
-        JSObject* callee = visitor->callee();
+        JSCell* callee = visitor->callee();
         if (callee != m_targetCallee)
             return StackVisitor::Continue;
 
@@ -277,7 +277,7 @@ public:
 
     StackVisitor::Status operator()(StackVisitor& visitor) const
     {
-        JSObject* callee = visitor->callee();
+        JSCell* callee = visitor->callee();
 
         if (callee && callee->inherits(JSBoundFunction::info()))
             return StackVisitor::Continue;
index ea7ec5e..0a81ae7 100644 (file)
@@ -35,17 +35,20 @@ namespace JSC {
 
 intptr_t StackFrame::sourceID() const
 {
-    if (!codeBlock)
+    if (!m_codeBlock)
         return noSourceID;
-    return codeBlock->ownerScriptExecutable()->sourceID();
+    return m_codeBlock->ownerScriptExecutable()->sourceID();
 }
 
 String StackFrame::sourceURL() const
 {
-    if (!codeBlock)
+    if (!m_codeBlock) {
+        if (m_callee && m_callee->isAnyWasmCallee())
+            return ASCIILiteral("[wasm code]");
         return ASCIILiteral("[native code]");
+    }
 
-    String sourceURL = codeBlock->ownerScriptExecutable()->sourceURL();
+    String sourceURL = m_codeBlock->ownerScriptExecutable()->sourceURL();
     if (!sourceURL.isNull())
         return sourceURL;
     return emptyString();
@@ -53,8 +56,8 @@ String StackFrame::sourceURL() const
 
 String StackFrame::functionName(VM& vm) const
 {
-    if (codeBlock) {
-        switch (codeBlock->codeType()) {
+    if (m_codeBlock) {
+        switch (m_codeBlock->codeType()) {
         case EvalCode:
             return ASCIILiteral("eval code");
         case ModuleCode:
@@ -68,14 +71,18 @@ String StackFrame::functionName(VM& vm) const
         }
     }
     String name;
-    if (callee)
-        name = getCalculatedDisplayName(vm, callee.get()).impl();
+    if (m_callee) {
+        if (m_callee->isObject())
+            name = getCalculatedDisplayName(vm, jsCast<JSObject*>(m_callee.get())).impl();
+        else if (m_callee->isAnyWasmCallee())
+            return ASCIILiteral("<wasm>");
+    }
     return name.isNull() ? emptyString() : name;
 }
 
 void StackFrame::computeLineAndColumn(unsigned& line, unsigned& column) const
 {
-    if (!codeBlock) {
+    if (!m_codeBlock) {
         line = 0;
         column = 0;
         return;
@@ -84,9 +91,9 @@ void StackFrame::computeLineAndColumn(unsigned& line, unsigned& column) const
     int divot = 0;
     int unusedStartOffset = 0;
     int unusedEndOffset = 0;
-    codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divot, unusedStartOffset, unusedEndOffset, line, column);
+    m_codeBlock->expressionRangeForBytecodeOffset(m_bytecodeOffset, divot, unusedStartOffset, unusedEndOffset, line, column);
 
-    ScriptExecutable* executable = codeBlock->ownerScriptExecutable();
+    ScriptExecutable* executable = m_codeBlock->ownerScriptExecutable();
     if (executable->hasOverrideLineNumber())
         line = executable->overrideLineNumber();
 }
@@ -101,7 +108,7 @@ String StackFrame::toString(VM& vm) const
         if (!functionName.isEmpty())
             traceBuild.append('@');
         traceBuild.append(sourceURL);
-        if (codeBlock) {
+        if (hasLineAndColumnInfo()) {
             unsigned line;
             unsigned column;
             computeLineAndColumn(line, column);
index 3c137c1..5ba542d 100644 (file)
@@ -32,18 +32,39 @@ namespace JSC {
 class CodeBlock;
 class JSObject;
 
-struct StackFrame {
-    Strong<JSObject> callee;
-    Strong<CodeBlock> codeBlock;
-    unsigned bytecodeOffset;
-    
-    bool isNative() const { return !codeBlock; }
+class StackFrame {
+public:
+    StackFrame(VM& vm, JSCell* callee)
+        : m_callee(vm, callee)
+    { }
+
+    StackFrame(VM& vm, JSCell* callee, CodeBlock* codeBlock, unsigned bytecodeOffset)
+        : m_callee(vm, callee)
+        , m_codeBlock(vm, codeBlock)
+        , m_bytecodeOffset(bytecodeOffset)
+    { }
+
+    bool hasLineAndColumnInfo() const { return !!m_codeBlock; }
     
     void computeLineAndColumn(unsigned& line, unsigned& column) const;
     String functionName(VM&) const;
     intptr_t sourceID() const;
     String sourceURL() const;
     String toString(VM&) const;
+
+    bool hasBytecodeOffset() const { return m_bytecodeOffset != UINT_MAX; }
+    unsigned bytecodeOffset()
+    {
+        ASSERT(m_bytecodeOffset != UINT_MAX);
+        return m_bytecodeOffset;
+    }
+
+
+private:
+    Strong<JSCell> m_callee { };
+    Strong<CodeBlock> m_codeBlock { };
+    unsigned m_bytecodeOffset { UINT_MAX };
+    
 };
 
 } // namespace JSC
index 13b8dd2..636f2eb 100644 (file)
@@ -110,7 +110,7 @@ class ShadowChicken;
 class ScriptExecutable;
 class SourceProvider;
 class SourceProviderCache;
-struct StackFrame;
+class StackFrame;
 class Structure;
 #if ENABLE(REGEXP_TRACING)
 class RegExp;
index a51e82a..a6c11bd 100644 (file)
 #include "B3VariableValue.h"
 #include "B3WasmAddressValue.h"
 #include "B3WasmBoundsCheckValue.h"
+#include "ExceptionScope.h"
+#include "FrameTracers.h"
+#include "JITExceptions.h"
+#include "JSCInlines.h"
 #include "JSWebAssemblyInstance.h"
 #include "JSWebAssemblyModule.h"
+#include "JSWebAssemblyRuntimeError.h"
 #include "VirtualRegister.h"
 #include "WasmCallingConvention.h"
 #include "WasmFunctionParser.h"
@@ -224,13 +229,45 @@ B3IRGenerator::B3IRGenerator(const MemoryInformation& memory, Procedure& procedu
             m_proc.pinRegister(info.sizeRegister);
 
         m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned) {
+            AllowMacroScratchRegisterUsage allowScratch(jit);
             ASSERT_UNUSED(pinnedGPR, m_memorySizeGPR == pinnedGPR);
-            // FIXME: This should unwind the stack and throw a JS exception. See: https://bugs.webkit.org/show_bug.cgi?id=163351
-            jit.breakpoint();
+            jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer();
+
+            jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+
+            CCallHelpers::Call call = jit.call();
+            jit.jumpToExceptionHandler();
+
+            jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+                void (*throwMemoryException)(ExecState*) = [] (ExecState* exec) {
+                    VM* vm = &exec->vm();
+                    NativeCallFrameTracer tracer(vm, exec);
+
+                    {
+                        auto throwScope = DECLARE_THROW_SCOPE(*vm);
+                        JSGlobalObject* globalObject = vm->topJSWebAssemblyInstance->globalObject();
+
+                        JSWebAssemblyRuntimeError* error = JSWebAssemblyRuntimeError::create(
+                            exec, globalObject->WebAssemblyRuntimeErrorStructure(), "Out of bounds memory access");
+                        throwException(exec, throwScope, error);
+                    }
+
+                    genericUnwind(vm, exec);
+                    ASSERT(!!vm->callFrameForCatch);
+                };
+
+                linkBuffer.link(call, throwMemoryException);
+            });
         });
+
+        B3::PatchpointValue* foo = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, Origin());
+        foo->setGenerator(
+            [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
+                AllowMacroScratchRegisterUsage allowScratch(jit);
+            });
     }
 
-    wasmCallingConvention().setupFrameInPrologue(compilation, m_proc, Origin(), m_currentBlock);
+    wasmCallingConvention().setupFrameInPrologue(&compilation->wasmCalleeMoveLocation, m_proc, Origin(), m_currentBlock);
 
     m_functionIndexSpaceValue = m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), functionIndexSpace.buffer.get());
 }
@@ -718,21 +755,25 @@ void B3IRGenerator::dump(const Vector<ControlEntry>& controlStack, const Express
     dataLogLn("\n");
 }
 
-static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, const Signature* signature, MacroAssemblerCodePtr mainFunction, const MemoryInformation& memory)
+static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, WasmInternalFunction& function, const Signature* signature, MacroAssemblerCodePtr mainFunction, const MemoryInformation& memory)
 {
     Procedure proc;
     BasicBlock* block = proc.addBlock();
 
-    // Check argument count is sane.
-    Value* framePointer = block->appendNew<B3::Value>(proc, B3::FramePointer, Origin());
-    Value* offSetOfArgumentCount = block->appendNew<Const64Value>(proc, Origin(), CallFrameSlot::argumentCount * sizeof(Register));
-    Value* argumentCount = block->appendNew<MemoryValue>(proc, Load, Int32, Origin(),
-        block->appendNew<Value>(proc, Add, Origin(), framePointer, offSetOfArgumentCount));
+    Origin origin;
+
+    jscCallingConvention().setupFrameInPrologue(&function.jsToWasmCalleeMoveLocation, proc, origin, block);
+
+    Value* framePointer = block->appendNew<B3::Value>(proc, B3::FramePointer, origin);
+    Value* offSetOfArgumentCount = block->appendNew<Const64Value>(proc, origin, CallFrameSlot::argumentCount * sizeof(Register));
+    Value* argumentCount = block->appendNew<MemoryValue>(proc, Load, Int32, origin,
+        block->appendNew<Value>(proc, Add, origin, framePointer, offSetOfArgumentCount));
+
+    Value* expectedArgumentCount = block->appendNew<Const32Value>(proc, origin, signature->arguments.size());
 
-    Value* expectedArgumentCount = block->appendNew<Const32Value>(proc, Origin(), signature->arguments.size());
+    CheckValue* argumentCountCheck = block->appendNew<CheckValue>(proc, Check, origin,
+        block->appendNew<Value>(proc, Above, origin, expectedArgumentCount, argumentCount));
 
-    CheckValue* argumentCountCheck = block->appendNew<CheckValue>(proc, Check, Origin(),
-        block->appendNew<Value>(proc, Above, Origin(), expectedArgumentCount, argumentCount));
     argumentCountCheck->setGenerator([] (CCallHelpers& jit, const StackmapGenerationParams&) {
         jit.breakpoint();
     });
@@ -747,19 +788,19 @@ static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, const Signatur
             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)));
+            sizes.append(block->appendNew<Value>(proc, Sub, origin, size,
+                block->appendNew<Const32Value>(proc, origin, info.sizeOffset)));
         }
     }
 
     // Get our arguments.
     Vector<Value*> arguments;
-    jscCallingConvention().loadArguments(signature->arguments, proc, block, Origin(), [&] (Value* argument, unsigned) {
+    jscCallingConvention().loadArguments(signature->arguments, proc, block, origin, [&] (Value* argument, unsigned) {
         arguments.append(argument);
     });
 
     // Move the arguments into place.
-    Value* result = wasmCallingConvention().setupCall(proc, block, Origin(), arguments, toB3Type(signature->returnType), [&] (PatchpointValue* patchpoint) {
+    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)));
@@ -780,22 +821,24 @@ static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, const Signatur
     // Return the result, if needed.
     switch (signature->returnType) {
     case Wasm::Void:
-        block->appendNewControlValue(proc, B3::Return, Origin());
+        block->appendNewControlValue(proc, B3::Return, origin);
         break;
     case Wasm::F32:
     case Wasm::F64:
-        result = block->appendNew<Value>(proc, BitwiseCast, Origin(), result);
+        result = block->appendNew<Value>(proc, BitwiseCast, origin, result);
         FALLTHROUGH;
     case Wasm::I32:
     case Wasm::I64:
-        block->appendNewControlValue(proc, B3::Return, Origin(), result);
+        block->appendNewControlValue(proc, B3::Return, origin, result);
         break;
     case Wasm::Func:
     case Wasm::Anyfunc:
         RELEASE_ASSERT_NOT_REACHED();
     }
 
-    return std::make_unique<Compilation>(vm, proc);
+    auto jsEntrypoint = std::make_unique<Compilation>(vm, proc);
+    function.jsToWasmEntrypoint.calleeSaveRegisters = proc.calleeSaveRegisters();
+    return jsEntrypoint;
 }
 
 std::unique_ptr<WasmInternalFunction> parseAndCompile(VM& vm, const uint8_t* functionStart, size_t functionLength, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& info, unsigned optLevel)
@@ -817,8 +860,9 @@ std::unique_ptr<WasmInternalFunction> parseAndCompile(VM& vm, const uint8_t* fun
     if (verbose)
         dataLog("Post SSA: ", procedure);
 
-    result->code = std::make_unique<Compilation>(vm, procedure, optLevel);
-    result->jsToWasmEntryPoint = createJSToWasmWrapper(vm, signature, result->code->code(), info.memory);
+    result->wasmEntrypoint.compilation = std::make_unique<Compilation>(vm, procedure, optLevel);
+    result->wasmEntrypoint.calleeSaveRegisters = procedure.calleeSaveRegisters();
+    result->jsToWasmEntrypoint.compilation = createJSToWasmWrapper(vm, *result, signature, result->wasmEntrypoint.compilation->code(), info.memory);
     return result;
 }
 
index 4c06e5a..b735937 100644 (file)
@@ -83,7 +83,7 @@ private:
     }
 
 public:
-    void setupFrameInPrologue(WasmInternalFunction* compilation, B3::Procedure& proc, B3::Origin origin, B3::BasicBlock* block) const
+    void setupFrameInPrologue(CodeLocationDataLabelPtr* calleeMoveLocation, B3::Procedure& proc, B3::Origin origin, B3::BasicBlock* block) const
     {
         static_assert(CallFrameSlot::callee * sizeof(Register) < headerSize, "We rely on this here for now.");
         static_assert(CallFrameSlot::codeBlock * sizeof(Register) < headerSize, "We rely on this here for now.");
@@ -96,7 +96,7 @@ public:
                 GPRReg result = params[0].gpr();
                 MacroAssembler::DataLabelPtr moveLocation = jit.moveWithPatch(MacroAssembler::TrustedImmPtr(nullptr), result);
                 jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
-                    compilation->calleeMoveLocation = linkBuffer.locationOf(moveLocation);
+                    *calleeMoveLocation = linkBuffer.locationOf(moveLocation);
                 });
             });
 
index ab86b39..d992dba 100644 (file)
@@ -32,6 +32,7 @@
 #include "CodeLocation.h"
 #include "Identifier.h"
 #include "MacroAssemblerCodeRef.h"
+#include "RegisterAtOffsetList.h"
 #include "WasmMemoryInformation.h"
 #include "WasmOps.h"
 #include "WasmPageCount.h"
@@ -166,10 +167,17 @@ struct UnlinkedWasmToWasmCall {
     size_t functionIndex;
 };
 
+struct Entrypoint {
+    std::unique_ptr<B3::Compilation> compilation;
+    RegisterAtOffsetList calleeSaveRegisters;
+};
+
 struct WasmInternalFunction {
-    CodeLocationDataLabelPtr calleeMoveLocation;
-    std::unique_ptr<B3::Compilation> code;
-    std::unique_ptr<B3::Compilation> jsToWasmEntryPoint;
+    CodeLocationDataLabelPtr wasmCalleeMoveLocation;
+    CodeLocationDataLabelPtr jsToWasmCalleeMoveLocation;
+
+    Entrypoint wasmEntrypoint;
+    Entrypoint jsToWasmEntrypoint;
 };
 
 typedef MacroAssemblerCodeRef WasmToJSStub;
index 6b97b0c..f19ece0 100644 (file)
@@ -38,15 +38,15 @@ Memory::Memory(PageCount initial, PageCount maximum)
     , m_maximum(maximum)
     // FIXME: If we add signal based bounds checking then we need extra space for overflow on load.
     // see: https://bugs.webkit.org/show_bug.cgi?id=162693
-    , m_mappedCapacity(PageCount::max().bytes())
 {
     RELEASE_ASSERT(!maximum || maximum >= initial); // This should be guaranteed by our caller.
 
+    m_mappedCapacity = m_capacity;
     // 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);
     if (result == MAP_FAILED) {
         // Try again with a different number.
-        m_mappedCapacity = m_capacity;
+        m_mappedCapacity = m_size;
         result = mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
         if (result == MAP_FAILED)
             return;
index 946e5e0..b411f34 100644 (file)
@@ -52,6 +52,8 @@ 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; }
 
index d6c6083..68329e5 100644 (file)
@@ -130,7 +130,7 @@ void Plan::run()
 
         unlinkedWasmToWasmCalls.uncheckedAppend(Vector<UnlinkedWasmToWasmCall>());
         m_wasmInternalFunctions.uncheckedAppend(parseAndCompile(*m_vm, functionStart, functionLength, signature, unlinkedWasmToWasmCalls.at(functionIndex), m_functionIndexSpace, *m_moduleInformation));
-        m_functionIndexSpace.buffer.get()[functionIndexSpace].code = m_wasmInternalFunctions[functionIndex]->code->code().executableAddress();
+        m_functionIndexSpace.buffer.get()[functionIndexSpace].code = m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation->code().executableAddress();
     }
 
     // Patch the call sites for each WebAssembly function.
@@ -142,20 +142,19 @@ void Plan::run()
     m_failed = false;
 }
 
-void Plan::initializeCallees(JSGlobalObject* globalObject, std::function<void(unsigned, JSWebAssemblyCallee*)> callback)
+void Plan::initializeCallees(JSGlobalObject* globalObject, std::function<void(unsigned, JSWebAssemblyCallee*, JSWebAssemblyCallee*)> callback)
 {
     ASSERT(!failed());
     for (unsigned internalFunctionIndex = 0; internalFunctionIndex < m_wasmInternalFunctions.size(); ++internalFunctionIndex) {
         WasmInternalFunction* function = m_wasmInternalFunctions[internalFunctionIndex].get();
-        CodeLocationDataLabelPtr calleeMoveLocation = function->calleeMoveLocation;
-        JSWebAssemblyCallee* callee = JSWebAssemblyCallee::create(globalObject->vm(), WTFMove(function->code), WTFMove(function->jsToWasmEntryPoint));
 
-        MacroAssembler::repatchPointer(calleeMoveLocation, callee);
+        JSWebAssemblyCallee* jsEntrypointCallee = JSWebAssemblyCallee::create(globalObject->vm(), WTFMove(function->jsToWasmEntrypoint));
+        MacroAssembler::repatchPointer(function->jsToWasmCalleeMoveLocation, jsEntrypointCallee);
 
-        if (verbose)
-            dataLogLn("Made Wasm callee: ", RawPointer(callee));
+        JSWebAssemblyCallee* wasmEntrypointCallee = JSWebAssemblyCallee::create(globalObject->vm(), WTFMove(function->wasmEntrypoint));
+        MacroAssembler::repatchPointer(function->wasmCalleeMoveLocation, wasmEntrypointCallee);
 
-        callback(internalFunctionIndex, callee);
+        callback(internalFunctionIndex, jsEntrypointCallee, wasmEntrypointCallee);
     }
 }
 
index 3cdbb65..2aedebd 100644 (file)
@@ -50,7 +50,7 @@ public:
 
     JS_EXPORT_PRIVATE void run();
 
-    JS_EXPORT_PRIVATE void initializeCallees(JSGlobalObject*, std::function<void(unsigned, JSWebAssemblyCallee*)>);
+    JS_EXPORT_PRIVATE void initializeCallees(JSGlobalObject*, std::function<void(unsigned, JSWebAssemblyCallee*, JSWebAssemblyCallee*)>);
 
     bool WARN_UNUSED_RETURN failed() const { return m_failed; }
     const String& errorMessage() const
@@ -71,12 +71,6 @@ public:
         return m_wasmInternalFunctions.size();
     }
 
-    B3::Compilation* jsToWasmEntryPointForFunction(size_t i) const
-    {
-        ASSERT(i > m_wasmToJSStubs.size());
-        return m_wasmInternalFunctions.at(i - m_wasmToJSStubs.size())->jsToWasmEntryPoint.get();
-    }
-
     std::unique_ptr<ModuleInformation>&& takeModuleInformation()
     {
         RELEASE_ASSERT(!failed());
index 39b1005..4db9269 100644 (file)
@@ -38,12 +38,11 @@ JSWebAssemblyCallee::JSWebAssemblyCallee(VM& vm)
     : Base(vm, vm.webAssemblyCalleeStructure.get())
 { }
 
-void JSWebAssemblyCallee::finishCreation(VM& vm, std::unique_ptr<B3::Compilation>&& code, std::unique_ptr<B3::Compilation>&& jsToWasmEntryPoint)
+void JSWebAssemblyCallee::finishCreation(VM& vm, Wasm::Entrypoint&& entrypoint)
 {
     Base::finishCreation(vm);
 
-    m_code = WTFMove(code);
-    m_jsToWasmEntryPoint = WTFMove(jsToWasmEntryPoint);
+    m_entrypoint = WTFMove(entrypoint);
 }
 
 void JSWebAssemblyCallee::destroy(JSCell* cell)
index 2be9952..b8777da 100644 (file)
@@ -27,7 +27,9 @@
 
 #if ENABLE(WEBASSEMBLY)
 
-#include "JSCallee.h"
+#include "JSCell.h"
+#include "RegisterAtOffsetList.h"
+#include "Structure.h"
 #include "WasmFormat.h"
 
 namespace JSC {
@@ -37,10 +39,10 @@ public:
     typedef JSCell Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
 
-    static JSWebAssemblyCallee* create(VM& vm, std::unique_ptr<B3::Compilation>&& code, std::unique_ptr<B3::Compilation>&& jsToWasmEntryPoint)
+    static JSWebAssemblyCallee* create(VM& vm, Wasm::Entrypoint&& entrypoint)
     {
         JSWebAssemblyCallee* callee = new (NotNull, allocateCell<JSWebAssemblyCallee>(vm.heap)) JSWebAssemblyCallee(vm);
-        callee->finishCreation(vm, std::forward<std::unique_ptr<B3::Compilation>>(code), std::forward<std::unique_ptr<B3::Compilation>>(jsToWasmEntryPoint));
+        callee->finishCreation(vm, WTFMove(entrypoint));
         return callee;
     }
 
@@ -53,14 +55,15 @@ public:
     static const bool needsDestruction = true;
     static void destroy(JSCell*);
 
-    void* jsToWasmEntryPoint() { return m_jsToWasmEntryPoint->code().executableAddress(); }
+    void* entrypoint() { return m_entrypoint.compilation->code().executableAddress(); }
+
+    RegisterAtOffsetList* calleeSaveRegisters() { return &m_entrypoint.calleeSaveRegisters; }
 
 private:
-    void finishCreation(VM&, std::unique_ptr<B3::Compilation>&&, std::unique_ptr<B3::Compilation>&&);
+    void finishCreation(VM&, Wasm::Entrypoint&&);
     JSWebAssemblyCallee(VM&);
 
-    std::unique_ptr<B3::Compilation> m_code;
-    std::unique_ptr<B3::Compilation> m_jsToWasmEntryPoint;
+    Wasm::Entrypoint m_entrypoint;
 };
 
 } // namespace JSC
index 83a3973..337ce03 100644 (file)
@@ -58,7 +58,7 @@ JSWebAssemblyModule::JSWebAssemblyModule(VM& vm, Structure* structure, std::uniq
     , m_functionIndexSpace(WTFMove(functionIndexSpace))
     , m_calleeCount(calleeCount)
 {
-    memset(callees(), 0, m_calleeCount * sizeof(WriteBarrier<JSWebAssemblyCallee>));
+    memset(callees(), 0, m_calleeCount * sizeof(WriteBarrier<JSWebAssemblyCallee>) * 2);
 }
 
 void JSWebAssemblyModule::finishCreation(VM& vm, SymbolTable* exportSymbolTable)
@@ -75,17 +75,25 @@ void JSWebAssemblyModule::destroy(JSCell* cell)
 
 void JSWebAssemblyModule::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
-    auto* thisObject = jsCast<JSWebAssemblyModule*>(cell);
+    JSWebAssemblyModule* thisObject = jsCast<JSWebAssemblyModule*>(cell);
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
 
     Base::visitChildren(thisObject, visitor);
     visitor.append(&thisObject->m_exportSymbolTable);
-    for (auto iter = thisObject->m_callLinkInfos.begin(); !!iter; ++iter)
-        (*iter)->visitWeak(*thisObject->vm());
-    for (unsigned i = 0; i < thisObject->m_calleeCount; i++) {
+    for (unsigned i = 0; i < thisObject->m_calleeCount * 2; i++) {
         WriteBarrier<JSWebAssemblyCallee>* callee = &thisObject->callees()[i];
         visitor.append(callee);
     }
+
+    visitor.addUnconditionalFinalizer(&thisObject->m_unconditionalFinalizer);
+}
+
+void JSWebAssemblyModule::UnconditionalFinalizer::finalizeUnconditionally()
+{
+    JSWebAssemblyModule* thisObject = bitwise_cast<JSWebAssemblyModule*>(
+        bitwise_cast<char*>(this) - OBJECT_OFFSETOF(JSWebAssemblyModule, m_unconditionalFinalizer));
+    for (auto iter = thisObject->m_callLinkInfos.begin(); !!iter; ++iter)
+        (*iter)->visitWeak(*thisObject->vm());
 }
 
 } // namespace JSC
index 7be607a..cc1257b 100644 (file)
 
 #include "JSDestructibleObject.h"
 #include "JSObject.h"
+#include "JSWebAssemblyCallee.h"
+#include "UnconditionalFinalizer.h"
 #include "WasmFormat.h"
 #include <wtf/Bag.h>
 #include <wtf/Vector.h>
 
 namespace JSC {
 
-class JSWebAssemblyCallee;
 class SymbolTable;
 
 class JSWebAssemblyModule : public JSDestructibleObject {
@@ -52,7 +53,7 @@ public:
     Wasm::Signature* signatureForFunctionIndexSpace(unsigned functionIndexSpace) const { ASSERT(functionIndexSpace < m_functionIndexSpace.size); return m_functionIndexSpace.buffer.get()[functionIndexSpace].signature; }
     unsigned importCount() const { return m_wasmToJSStubs.size(); }
 
-    JSWebAssemblyCallee* calleeFromFunctionIndexSpace(unsigned functionIndexSpace)
+    JSWebAssemblyCallee* jsEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
     {
         RELEASE_ASSERT(functionIndexSpace >= importCount());
         unsigned calleeIndex = functionIndexSpace - importCount();
@@ -60,6 +61,26 @@ public:
         return callees()[calleeIndex].get();
     }
 
+    JSWebAssemblyCallee* wasmEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
+    {
+        RELEASE_ASSERT(functionIndexSpace >= importCount());
+        unsigned calleeIndex = functionIndexSpace - importCount();
+        RELEASE_ASSERT(calleeIndex < m_calleeCount);
+        return callees()[calleeIndex + m_calleeCount].get();
+    }
+
+    void setJSEntrypointCallee(VM& vm, unsigned calleeIndex, JSWebAssemblyCallee* callee)
+    {
+        RELEASE_ASSERT(calleeIndex < m_calleeCount);
+        callees()[calleeIndex].set(vm, this, callee);
+    }
+
+    void setWasmEntrypointCallee(VM& vm, unsigned calleeIndex, JSWebAssemblyCallee* callee)
+    {
+        RELEASE_ASSERT(calleeIndex < m_calleeCount);
+        callees()[calleeIndex + m_calleeCount].set(vm, this, callee);
+    }
+
     WriteBarrier<JSWebAssemblyCallee>* callees()
     {
         return bitwise_cast<WriteBarrier<JSWebAssemblyCallee>*>(bitwise_cast<char*>(this) + offsetOfCallees());
@@ -81,9 +102,14 @@ private:
 
     static size_t allocationSize(unsigned numCallees)
     {
-        return offsetOfCallees() + sizeof(WriteBarrier<JSWebAssemblyCallee>) * numCallees;
+        return offsetOfCallees() + sizeof(WriteBarrier<JSWebAssemblyCallee>) * numCallees * 2;
     }
 
+    class UnconditionalFinalizer : public JSC::UnconditionalFinalizer { 
+        void finalizeUnconditionally() override;
+    };
+
+    UnconditionalFinalizer m_unconditionalFinalizer;
     std::unique_ptr<Wasm::ModuleInformation> m_moduleInformation;
     Bag<CallLinkInfo> m_callLinkInfos;
     WriteBarrier<SymbolTable> m_exportSymbolTable;
index 7ebe7c4..935930a 100644 (file)
@@ -35,7 +35,7 @@ class JSWebAssemblyRuntimeError : public ErrorInstance {
 public:
     typedef ErrorInstance Base;
 
-    static JSWebAssemblyRuntimeError* create(ExecState*, Structure*, const String&, bool);
+    static JSWebAssemblyRuntimeError* create(ExecState*, Structure*, const String&, bool useCurrentFrame = true);
     static JSWebAssemblyRuntimeError* create(ExecState* exec, Structure* structure, JSValue message, bool useCurrentFrame)
     {
         return create(exec, structure, message.isUndefined() ? String() : message.toString(exec)->value(exec), useCurrentFrame);
index 106801b..a55f960 100644 (file)
@@ -116,7 +116,8 @@ EncodedJSValue WebAssemblyFunction::call(VM& vm, ProtoCallFrame* protoCallFrame)
 
     JSWebAssemblyInstance* prevJSWebAssemblyInstance = vm.topJSWebAssemblyInstance;
     vm.topJSWebAssemblyInstance = instance();
-    EncodedJSValue rawResult = vmEntryToWasm(webAssemblyCallee()->jsToWasmEntryPoint(), &vm, protoCallFrame);
+    ASSERT(instance());
+    EncodedJSValue rawResult = vmEntryToWasm(webAssemblyCallee()->entrypoint(), &vm, protoCallFrame);
     vm.topJSWebAssemblyInstance = prevJSWebAssemblyInstance;
 
     // FIXME is this correct? https://bugs.webkit.org/show_bug.cgi?id=164876
index 791fd37..bf0aac2 100644 (file)
@@ -193,6 +193,8 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
             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())
+                return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec)));
             instance->setMemory(vm,
                JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory)));
         }
index 0c27f45..cf95311 100644 (file)
@@ -107,6 +107,8 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyMemory(ExecState* exec
     }
 
     std::unique_ptr<Wasm::Memory> memory = std::make_unique<Wasm::Memory>(initialPageCount, maximumPageCount);
+    if (!memory->isValid())
+        return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec)));
 
     return JSValue::encode(JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory)));
 }
index 8b9ca07..de8f4a3 100644 (file)
@@ -92,8 +92,9 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyModule(ExecState* stat
     unsigned calleeCount = plan.internalFunctionCount();
     JSWebAssemblyModule* result = JSWebAssemblyModule::create(vm, structure, plan.takeModuleInformation(), plan.takeCallLinkInfos(), plan.takeWasmToJSStubs(), plan.takeFunctionIndexSpace(), exportSymbolTable, calleeCount);
     plan.initializeCallees(state->jsCallee()->globalObject(), 
-        [&] (unsigned calleeIndex, JSWebAssemblyCallee* callee) {
-            result->callees()[calleeIndex].set(vm, result, callee);
+        [&] (unsigned calleeIndex, JSWebAssemblyCallee* jsEntrypointCallee, JSWebAssemblyCallee* wasmEntrypointCallee) {
+            result->setJSEntrypointCallee(vm, calleeIndex, jsEntrypointCallee);
+            result->setWasmEntrypointCallee(vm, calleeIndex, wasmEntrypointCallee);
         });
     return JSValue::encode(result);
 }
index 67d5e22..5983c03 100644 (file)
@@ -136,7 +136,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
             //     a. Let func be an Exported Function Exotic Object created from c.
             //     b. Append func to funcs.
             //     c. Return func.
-            JSWebAssemblyCallee* wasmCallee = module->calleeFromFunctionIndexSpace(exp.functionIndex);
+            JSWebAssemblyCallee* wasmCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(exp.functionIndex);
             Wasm::Signature* signature = module->signatureForFunctionIndexSpace(exp.functionIndex);
             WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), exp.field.string(), instance, wasmCallee, signature);
             exportedValue = function;
@@ -174,7 +174,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
         // FIXME can start call imports / tables? This assumes not. https://github.com/WebAssembly/design/issues/896
         if (!m_startFunction.get()) {
             // The start function wasn't added above. It must be a purely internal function.
-            JSWebAssemblyCallee* wasmCallee = module->calleeFromFunctionIndexSpace(startFunctionIndexSpace);
+            JSWebAssemblyCallee* wasmCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
             WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), "start", instance, wasmCallee, signature);
             m_startFunction.set(vm, this, function);
         }
index 0de49f7..cbe44d0 100644 (file)
@@ -1,3 +1,13 @@
+2016-12-11  Saam Barati  <sbarati@apple.com>
+
+        We should be able to throw exceptions from Wasm code and when Wasm frames are on the stack
+        https://bugs.webkit.org/show_bug.cgi?id=165429
+
+        Reviewed by Keith Miller.
+
+        * bindings/js/JSDOMBinding.cpp:
+        (WebCore::GetCallerGlobalObjectFunctor::operator()):
+
 2016-12-11  Darin Adler  <darin@apple.com>
 
         Remove uses of Dictionary in WebRTC IDL files
index c8ff77f..468a4f9 100644 (file)
@@ -759,7 +759,11 @@ public:
             m_globalObject = codeBlock->globalObject();
         else {
             ASSERT(visitor->callee());
-            m_globalObject = visitor->callee()->globalObject();
+            // FIXME: Callee is not an object if the caller is Web Assembly.
+            // Figure out what to do here. We can probably get the global object
+            // from the top-most Wasm Instance. https://bugs.webkit.org/show_bug.cgi?id=165721
+            if (visitor->callee()->isObject())
+                m_globalObject = jsCast<JSObject*>(visitor->callee())->globalObject();
         }
         return StackVisitor::Done;
     }