Skip to content

Commit 1e66efc

Browse files
committed
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. 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()): Canonical link: https://commits.webkit.org/183352@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@209696 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent cf037d6 commit 1e66efc

41 files changed

Lines changed: 748 additions & 166 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

JSTests/ChangeLog

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
2016-12-11 Saam Barati <sbarati@apple.com>
2+
3+
We should be able to throw exceptions from Wasm code and when Wasm frames are on the stack
4+
https://bugs.webkit.org/show_bug.cgi?id=165429
5+
6+
Reviewed by Keith Miller.
7+
8+
* wasm/function-tests/trap-load.js: Added.
9+
(assert):
10+
(wasmFrameCountFromError):
11+
(i.catch):
12+
(assert.continuation):
13+
* wasm/function-tests/trap-store.js: Added.
14+
(import.Builder.from.string_appeared_here.assert):
15+
(i.catch):
16+
(assert.continuation):
17+
(assert):
18+
* wasm/js-api/test_memory_constructor.js:
19+
(assert):
20+
121
2016-12-10 Commit Queue <commit-queue@webkit.org>
222

323
Unreviewed, rolling out r209653, r209654, r209663, and
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import Builder from '../Builder.js'
2+
3+
const pageSize = 64 * 1024;
4+
const numPages = 10;
5+
6+
const builder = (new Builder())
7+
.Type().End()
8+
.Import()
9+
.Memory("a", "b", {initial: numPages})
10+
.End()
11+
.Function().End()
12+
.Export().Function("foo").End()
13+
.Code()
14+
.Function("foo", {params: ["i32"], ret: "i32"})
15+
.GetLocal(0)
16+
.I32Load(2, 0)
17+
.Return()
18+
.End()
19+
.End();
20+
21+
const bin = builder.WebAssembly().get();
22+
const module = new WebAssembly.Module(bin);
23+
const foo = new WebAssembly.Instance(module, {a: {b: new WebAssembly.Memory({initial: numPages})}}).exports.foo;
24+
25+
function assert(b) {
26+
if (!b)
27+
throw new Error("Bad")
28+
}
29+
30+
function wasmFrameCountFromError(e) {
31+
let stackFrames = e.stack.split("\n").filter((s) => s.indexOf("<wasm>@[wasm code]") !== -1);
32+
return stackFrames.length;
33+
}
34+
35+
for (let i = 0; i < 1000; i++) {
36+
let threw = false;
37+
try {
38+
foo(numPages * pageSize + 1);
39+
} catch(e) {
40+
assert(e instanceof WebAssembly.RuntimeError);
41+
assert(e.message === "Out of bounds memory access");
42+
threw = true;
43+
assert(wasmFrameCountFromError(e) === 2);
44+
}
45+
assert(threw);
46+
}
47+
48+
{
49+
const builder = (new Builder())
50+
.Type().End()
51+
.Import()
52+
.Memory("imp", "mem", {initial: numPages})
53+
.Function("imp", "func", { params: ["i32"] })
54+
.End()
55+
.Function().End()
56+
.Export().Function("foo").End()
57+
.Code()
58+
.Function("foo", {params: ["i32", "i32"]})
59+
.GetLocal(0)
60+
.I32Const(0)
61+
.I32Eq()
62+
.If("void", b =>
63+
b.GetLocal(1)
64+
.GetLocal(1)
65+
.I32Load(2, 0)
66+
.Br(0)
67+
.Else()
68+
.GetLocal(0)
69+
.Call(0)
70+
.Br(0)
71+
)
72+
.End()
73+
.End();
74+
75+
const bin = builder.WebAssembly().get();
76+
const module = new WebAssembly.Module(bin);
77+
const imp = {
78+
imp: {
79+
mem: new WebAssembly.Memory({initial: numPages}),
80+
func: continuation
81+
}
82+
};
83+
const foo = new WebAssembly.Instance(module, imp).exports.foo;
84+
const address = numPages*pageSize + 1;
85+
function continuation(x) {
86+
foo(x - 1, address);
87+
}
88+
89+
for (let i = 0; i < 10000; i++) {
90+
let threw = false;
91+
try {
92+
foo(25, address);
93+
} catch(e) {
94+
assert(e instanceof WebAssembly.RuntimeError);
95+
assert(e.message === "Out of bounds memory access");
96+
// There are 25 total calls, and each call does:
97+
// JS entry, wasm entry, js call stub.
98+
// The last call that traps just has JS entry and wasm entry.
99+
assert(wasmFrameCountFromError(e) === 25 * 3 + 2);
100+
threw = true;
101+
}
102+
assert(threw);
103+
}
104+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import Builder from '../Builder.js'
2+
3+
function assert(b) {
4+
if (!b)
5+
throw new Error("Bad")
6+
}
7+
8+
const pageSize = 64 * 1024;
9+
const numPages = 10;
10+
11+
{
12+
const builder = (new Builder())
13+
.Type().End()
14+
.Import()
15+
.Memory("a", "b", {initial: numPages})
16+
.End()
17+
.Function().End()
18+
.Export().Function("foo").End()
19+
.Code()
20+
.Function("foo", {params: ["i32", "i32"]})
21+
.GetLocal(1)
22+
.GetLocal(0)
23+
.I32Store(2, 0)
24+
.End()
25+
.End();
26+
27+
const bin = builder.WebAssembly().get();
28+
const module = new WebAssembly.Module(bin);
29+
const foo = new WebAssembly.Instance(module, {a: {b: new WebAssembly.Memory({initial: numPages})}}).exports.foo;
30+
31+
for (let i = 0; i < 10000; i++) {
32+
let threw = false;
33+
try {
34+
foo(i, numPages * pageSize + 1);
35+
} catch(e) {
36+
assert(e instanceof WebAssembly.RuntimeError);
37+
assert(e.message === "Out of bounds memory access");
38+
threw = true;
39+
}
40+
assert(threw);
41+
}
42+
}
43+
44+
45+
{
46+
const builder = (new Builder())
47+
.Type().End()
48+
.Import()
49+
.Memory("imp", "mem", {initial: numPages})
50+
.Function("imp", "func", { params: ["i32"] })
51+
.End()
52+
.Function().End()
53+
.Export().Function("foo").End()
54+
.Code()
55+
.Function("foo", {params: ["i32", "i32"]})
56+
.GetLocal(0)
57+
.I32Const(0)
58+
.I32Eq()
59+
.If("void", b =>
60+
b.GetLocal(1)
61+
.GetLocal(0)
62+
.I32Store(2, 0)
63+
.Br(0)
64+
.Else()
65+
.GetLocal(0)
66+
.Call(0)
67+
.Br(0)
68+
)
69+
.End()
70+
.End();
71+
72+
const bin = builder.WebAssembly().get();
73+
const module = new WebAssembly.Module(bin);
74+
const imp = {
75+
imp: {
76+
mem: new WebAssembly.Memory({initial: numPages}),
77+
func: continuation
78+
}
79+
};
80+
const foo = new WebAssembly.Instance(module, imp).exports.foo;
81+
const address = numPages*pageSize + 1;
82+
function continuation(x) {
83+
foo(x - 1, address);
84+
}
85+
86+
for (let i = 0; i < 10000; i++) {
87+
let threw = false;
88+
try {
89+
foo(25, address);
90+
} catch(e) {
91+
assert(e instanceof WebAssembly.RuntimeError);
92+
assert(e.message === "Out of bounds memory access");
93+
threw = true;
94+
}
95+
assert(threw);
96+
}
97+
}

JSTests/wasm/js-api/test_memory_constructor.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,12 @@ function testInvalidSize(description, propName) {
3939
testInvalidSize({initial: v}, "initial");
4040
}
4141

42-
// These should not throw.
43-
new WebAssembly.Memory({initial: maxPageCount});
44-
new WebAssembly.Memory({initial: maxPageCount, maximum: maxPageCount});
42+
try {
43+
new WebAssembly.Memory({initial: maxPageCount});
44+
new WebAssembly.Memory({initial: maxPageCount, maximum: maxPageCount});
45+
} catch(e) {
46+
// These might throw, since we're asking for a lot of memory.
47+
}
4548

4649
testInvalidInitial(2**31);
4750
testInvalidInitial(maxPageCount + 1);

Source/JavaScriptCore/API/JSContextRef.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ class BacktraceFunctor {
260260
if (m_remainingCapacityForFrameCapture) {
261261
// If callee is unknown, but we've not added any frame yet, we should
262262
// still add the frame, because something called us, and gave us arguments.
263-
JSObject* callee = visitor->callee();
263+
JSCell* callee = visitor->callee();
264264
if (!callee && visitor->index())
265265
return StackVisitor::Done;
266266

@@ -273,7 +273,7 @@ class BacktraceFunctor {
273273
builder.append(visitor->functionName());
274274
builder.appendLiteral("() at ");
275275
builder.append(visitor->sourceURL());
276-
if (visitor->isJSFrame()) {
276+
if (visitor->hasLineAndColumnInfo()) {
277277
builder.append(':');
278278
unsigned lineNumber;
279279
unsigned unusedColumn;

Source/JavaScriptCore/ChangeLog

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,137 @@
1+
2016-12-11 Saam Barati <sbarati@apple.com>
2+
3+
We should be able to throw exceptions from Wasm code and when Wasm frames are on the stack
4+
https://bugs.webkit.org/show_bug.cgi?id=165429
5+
6+
Reviewed by Keith Miller.
7+
8+
This patch teaches the stack walking runtime about wasm.
9+
To do this, I taught StackVisitor that a callee is not
10+
always an object.
11+
12+
To be able to unwind callee save registers properly, I've given
13+
JSWebAssemblyCallee a list of RegisterAtOffsetList for the callee
14+
saves that B3 saved in the prologue. Also, because we have two
15+
B3Compilations per wasm function, one for wasm entrypoint, and
16+
one for the JS entrypoint, I needed to create a callee for each
17+
because they each might spill callee save registers.
18+
19+
I also fixed a bug inside the Wasm::Memory constructor where we
20+
were trying to mmap the same number of bytes even after the first
21+
mmap failed. We should start by trying to mmap the maximum bytes,
22+
and if that fails, fall back to the specified initial bytes. However,
23+
the code was just mmapping the maximum twice. I've fixed that and
24+
also added a RELEASE_ASSERT_NOT_REACHED() for when the second mmap
25+
fails along with a FIXME to throw an OOM error.
26+
27+
There was a second bug I fixed where JSModuleRecord was calling
28+
visitWeak on its CallLinkInfos inside ::visitChldren(). It needs
29+
to do this after marking. I changed JSModuleRecord to do what
30+
CodeBlock does and call visitWeak on its CallLinkInfos inside
31+
an UnconditionalFinalizer.
32+
33+
* API/JSContextRef.cpp:
34+
(BacktraceFunctor::operator()):
35+
* inspector/ScriptCallStackFactory.cpp:
36+
(Inspector::createScriptCallStackFromException):
37+
* interpreter/CallFrame.cpp:
38+
(JSC::CallFrame::vmEntryGlobalObject):
39+
* interpreter/CallFrame.h:
40+
(JSC::ExecState::callee):
41+
* interpreter/Interpreter.cpp:
42+
(JSC::GetStackTraceFunctor::operator()):
43+
(JSC::UnwindFunctor::operator()):
44+
(JSC::UnwindFunctor::copyCalleeSavesToVMEntryFrameCalleeSavesBuffer):
45+
* interpreter/Interpreter.h:
46+
* interpreter/ShadowChicken.cpp:
47+
(JSC::ShadowChicken::update):
48+
* interpreter/StackVisitor.cpp:
49+
(JSC::StackVisitor::StackVisitor):
50+
(JSC::StackVisitor::readFrame):
51+
(JSC::StackVisitor::readNonInlinedFrame):
52+
(JSC::StackVisitor::readInlinedFrame):
53+
(JSC::StackVisitor::Frame::isWasmFrame):
54+
(JSC::StackVisitor::Frame::codeType):
55+
(JSC::StackVisitor::Frame::calleeSaveRegisters):
56+
(JSC::StackVisitor::Frame::functionName):
57+
(JSC::StackVisitor::Frame::sourceURL):
58+
(JSC::StackVisitor::Frame::toString):
59+
(JSC::StackVisitor::Frame::hasLineAndColumnInfo):
60+
(JSC::StackVisitor::Frame::setToEnd):
61+
* interpreter/StackVisitor.h:
62+
(JSC::StackVisitor::Frame::callee):
63+
(JSC::StackVisitor::Frame::isNativeFrame):
64+
(JSC::StackVisitor::Frame::isJSFrame): Deleted.
65+
* jsc.cpp:
66+
(callWasmFunction):
67+
(functionTestWasmModuleFunctions):
68+
* runtime/Error.cpp:
69+
(JSC::addErrorInfoAndGetBytecodeOffset):
70+
* runtime/JSCell.cpp:
71+
(JSC::JSCell::isAnyWasmCallee):
72+
* runtime/JSCell.h:
73+
* runtime/JSFunction.cpp:
74+
(JSC::RetrieveArgumentsFunctor::operator()):
75+
(JSC::RetrieveCallerFunctionFunctor::operator()):
76+
* runtime/StackFrame.cpp:
77+
(JSC::StackFrame::sourceID):
78+
(JSC::StackFrame::sourceURL):
79+
(JSC::StackFrame::functionName):
80+
(JSC::StackFrame::computeLineAndColumn):
81+
(JSC::StackFrame::toString):
82+
* runtime/StackFrame.h:
83+
(JSC::StackFrame::StackFrame):
84+
(JSC::StackFrame::hasLineAndColumnInfo):
85+
(JSC::StackFrame::hasBytecodeOffset):
86+
(JSC::StackFrame::bytecodeOffset):
87+
(JSC::StackFrame::isNative): Deleted.
88+
* runtime/VM.h:
89+
* wasm/WasmB3IRGenerator.cpp:
90+
(JSC::Wasm::B3IRGenerator::B3IRGenerator):
91+
(JSC::Wasm::createJSToWasmWrapper):
92+
(JSC::Wasm::parseAndCompile):
93+
* wasm/WasmCallingConvention.h:
94+
(JSC::Wasm::CallingConvention::setupFrameInPrologue):
95+
* wasm/WasmFormat.h:
96+
* wasm/WasmMemory.cpp:
97+
(JSC::Wasm::Memory::Memory):
98+
* wasm/WasmMemory.h:
99+
(JSC::Wasm::Memory::isValid):
100+
* wasm/WasmPlan.cpp:
101+
(JSC::Wasm::Plan::run):
102+
(JSC::Wasm::Plan::initializeCallees):
103+
* wasm/WasmPlan.h:
104+
(JSC::Wasm::Plan::jsToWasmEntryPointForFunction): Deleted.
105+
* wasm/js/JSWebAssemblyCallee.cpp:
106+
(JSC::JSWebAssemblyCallee::finishCreation):
107+
* wasm/js/JSWebAssemblyCallee.h:
108+
(JSC::JSWebAssemblyCallee::create):
109+
(JSC::JSWebAssemblyCallee::entrypoint):
110+
(JSC::JSWebAssemblyCallee::calleeSaveRegisters):
111+
(JSC::JSWebAssemblyCallee::jsToWasmEntryPoint): Deleted.
112+
* wasm/js/JSWebAssemblyModule.cpp:
113+
(JSC::JSWebAssemblyModule::JSWebAssemblyModule):
114+
(JSC::JSWebAssemblyModule::visitChildren):
115+
(JSC::JSWebAssemblyModule::UnconditionalFinalizer::finalizeUnconditionally):
116+
* wasm/js/JSWebAssemblyModule.h:
117+
(JSC::JSWebAssemblyModule::jsEntrypointCalleeFromFunctionIndexSpace):
118+
(JSC::JSWebAssemblyModule::wasmEntrypointCalleeFromFunctionIndexSpace):
119+
(JSC::JSWebAssemblyModule::setJSEntrypointCallee):
120+
(JSC::JSWebAssemblyModule::setWasmEntrypointCallee):
121+
(JSC::JSWebAssemblyModule::allocationSize):
122+
(JSC::JSWebAssemblyModule::calleeFromFunctionIndexSpace): Deleted.
123+
* wasm/js/JSWebAssemblyRuntimeError.h:
124+
* wasm/js/WebAssemblyFunction.cpp:
125+
(JSC::WebAssemblyFunction::call):
126+
* wasm/js/WebAssemblyInstanceConstructor.cpp:
127+
(JSC::constructJSWebAssemblyInstance):
128+
* wasm/js/WebAssemblyMemoryConstructor.cpp:
129+
(JSC::constructJSWebAssemblyMemory):
130+
* wasm/js/WebAssemblyModuleConstructor.cpp:
131+
(JSC::constructJSWebAssemblyModule):
132+
* wasm/js/WebAssemblyModuleRecord.cpp:
133+
(JSC::WebAssemblyModuleRecord::link):
134+
1135
2016-12-11 Filip Pizlo <fpizlo@apple.com>
2136

3137
Re-enable concurrent GC.

0 commit comments

Comments
 (0)