Diff
Modified: branches/safari-536.28-branch/LayoutTests/ChangeLog (134302 => 134303)
--- branches/safari-536.28-branch/LayoutTests/ChangeLog 2012-11-12 22:21:18 UTC (rev 134302)
+++ branches/safari-536.28-branch/LayoutTests/ChangeLog 2012-11-12 22:21:47 UTC (rev 134303)
@@ -1,5 +1,22 @@
2012-11-12 Lucas Forschler <[email protected]>
+ Merge r126718
+
+ 2012-08-26 Filip Pizlo <[email protected]>
+
+ Finally inlining should correctly track the catch context
+ https://bugs.webkit.org/show_bug.cgi?id=94986
+ <rdar://problem/11753784>
+
+ Reviewed by Sam Weinig.
+
+ * fast/js/jsc-test-list:
+ * fast/js/script-tests/throw-from-finally.js: Added.
+ * fast/js/throw-from-finally.html: Added.
+ * fast/js/throw-from-finally-expected.txt: Added.
+
+2012-11-12 Lucas Forschler <[email protected]>
+
Merge r131077
2012-10-11 Dan Bernstein <[email protected]>
@@ -11357,3 +11374,4 @@
.
.
.
+.
Modified: branches/safari-536.28-branch/LayoutTests/fast/js/jsc-test-list (134302 => 134303)
--- branches/safari-536.28-branch/LayoutTests/fast/js/jsc-test-list 2012-11-12 22:21:18 UTC (rev 134302)
+++ branches/safari-536.28-branch/LayoutTests/fast/js/jsc-test-list 2012-11-12 22:21:47 UTC (rev 134303)
@@ -258,6 +258,7 @@
fast/js/string-trim
fast/js/string_replace
fast/js/this-non-object-proto
+fast/js/throw-from-finally
fast/js/ToNumber
fast/js/toString-elision-trailing-comma
fast/js/tostring-exception-in-property-access
Copied: branches/safari-536.28-branch/LayoutTests/fast/js/script-tests/throw-from-finally.js (from rev 126718, trunk/LayoutTests/fast/js/script-tests/throw-from-finally.js) (0 => 134303)
--- branches/safari-536.28-branch/LayoutTests/fast/js/script-tests/throw-from-finally.js (rev 0)
+++ branches/safari-536.28-branch/LayoutTests/fast/js/script-tests/throw-from-finally.js 2012-11-12 22:21:47 UTC (rev 134303)
@@ -0,0 +1,58 @@
+description(
+"This tests that throwing from a finally block has the expected effect."
+);
+
+var events = [];
+
+try {
+ events.push("1:try");
+} finally {
+ events.push("1:finally");
+}
+
+try {
+ try {
+ throw "2:thingy";
+ } finally {
+ events.push("2:finally");
+ }
+} catch (e) {
+ events.push(e);
+}
+
+try {
+ throw "3:thingy";
+} catch (e) {
+ events.push(e);
+} finally {
+ events.push("3:finally");
+}
+
+try {
+ try {
+ throw "4:thingy";
+ } catch (e) {
+ events.push(e);
+ } finally {
+ events.push("4:finally");
+ throw "4:another thingy";
+ }
+} catch (e) {
+ events.push(e);
+}
+
+try {
+ for (;;) {
+ try {
+ continue;
+ } finally {
+ events.push("5:hi");
+ throw "5:wat";
+ }
+ }
+} catch (e) {
+ events.push(e);
+}
+
+shouldBe("\"\" + events", "\"1:try,1:finally,2:finally,2:thingy,3:thingy,3:finally,4:thingy,4:finally,4:another thingy,5:hi,5:wat\"");
+
Copied: branches/safari-536.28-branch/LayoutTests/fast/js/throw-from-finally-expected.txt (from rev 126718, trunk/LayoutTests/fast/js/throw-from-finally-expected.txt) (0 => 134303)
--- branches/safari-536.28-branch/LayoutTests/fast/js/throw-from-finally-expected.txt (rev 0)
+++ branches/safari-536.28-branch/LayoutTests/fast/js/throw-from-finally-expected.txt 2012-11-12 22:21:47 UTC (rev 134303)
@@ -0,0 +1,10 @@
+This tests that throwing from a finally block has the expected effect.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS "" + events is "1:try,1:finally,2:finally,2:thingy,3:thingy,3:finally,4:thingy,4:finally,4:another thingy,5:hi,5:wat"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Copied: branches/safari-536.28-branch/LayoutTests/fast/js/throw-from-finally.html (from rev 126718, trunk/LayoutTests/fast/js/throw-from-finally.html) (0 => 134303)
--- branches/safari-536.28-branch/LayoutTests/fast/js/throw-from-finally.html (rev 0)
+++ branches/safari-536.28-branch/LayoutTests/fast/js/throw-from-finally.html 2012-11-12 22:21:47 UTC (rev 134303)
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>
Modified: branches/safari-536.28-branch/Source/_javascript_Core/ChangeLog (134302 => 134303)
--- branches/safari-536.28-branch/Source/_javascript_Core/ChangeLog 2012-11-12 22:21:18 UTC (rev 134302)
+++ branches/safari-536.28-branch/Source/_javascript_Core/ChangeLog 2012-11-12 22:21:47 UTC (rev 134303)
@@ -1,5 +1,44 @@
2012-11-12 Lucas Forschler <[email protected]>
+ Merge r126718
+
+ 2012-08-24 Filip Pizlo <[email protected]>
+
+ Finally inlining should correctly track the catch context
+ https://bugs.webkit.org/show_bug.cgi?id=94986
+ <rdar://problem/11753784>
+
+ Reviewed by Sam Weinig.
+
+ This fixes two behaviors:
+
+ 1) Throwing from a finally block. Previously, we would seem to reenter the finally
+ block - though only once.
+
+ 2) Executing a finally block from some nested context, for example due to a
+ 'continue', 'break', or 'return' in the try. This would execute the finally
+ block in the context of of the try block, which could lead to either scope depth
+ mismatches or reexecutions of the finally block on throw, similarly to (1) but
+ for different reasons.
+
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC):
+ (JSC::BytecodeGenerator::pushFinallyContext):
+ (JSC::BytecodeGenerator::emitComplexJumpScopes):
+ (JSC::BytecodeGenerator::pushTry):
+ (JSC::BytecodeGenerator::popTryAndEmitCatch):
+ * bytecompiler/BytecodeGenerator.h:
+ (FinallyContext):
+ (TryData):
+ (JSC):
+ (TryContext):
+ (TryRange):
+ (BytecodeGenerator):
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::TryNode::emitBytecode):
+
+2012-11-12 Lucas Forschler <[email protected]>
+
Merge r129577
2012-09-25 Filip Pizlo <[email protected]>
@@ -69259,3 +69298,4 @@
.
.
.
+.
Modified: branches/safari-536.28-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (134302 => 134303)
--- branches/safari-536.28-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2012-11-12 22:21:18 UTC (rev 134302)
+++ branches/safari-536.28-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2012-11-12 22:21:47 UTC (rev 134303)
@@ -146,6 +146,24 @@
m_scopeNode->emitBytecode(*this);
+ for (unsigned i = 0; i < m_tryRanges.size(); ++i) {
+ TryRange& range = m_tryRanges[i];
+ ASSERT(range.tryData->targetScopeDepth != UINT_MAX);
+ HandlerInfo info = {
+ range.start->bind(0, 0), range.end->bind(0, 0),
+ range.tryData->target->bind(0, 0), range.tryData->targetScopeDepth
+#if ENABLE(JIT)
+ ,
+#if ENABLE(LLINT)
+ CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(bitwise_cast<void*>(&llint_op_catch)))
+#else
+ CodeLocationLabel()
+#endif
+#endif
+ };
+ m_codeBlock->addExceptionHandler(info);
+ }
+
m_codeBlock->instructions() = RefCountedArray<Instruction>(m_instructions);
if (s_dumpsGeneratedCode)
@@ -2019,6 +2037,7 @@
m_scopeContextStack.size(),
m_switchContextStack.size(),
m_forInContextStack.size(),
+ m_tryContextStack.size(),
m_labelScopes.size(),
m_finallyDepth,
m_dynamicScopeDepth
@@ -2152,14 +2171,18 @@
Vector<ControlFlowContext> savedScopeContextStack;
Vector<SwitchInfo> savedSwitchContextStack;
Vector<ForInContext> savedForInContextStack;
+ Vector<TryContext> poppedTryContexts;
SegmentedVector<LabelScope, 8> savedLabelScopes;
while (topScope > bottomScope && topScope->isFinallyBlock) {
+ RefPtr<Label> beforeFinally = emitLabel(newLabel().get());
+
// Save the current state of the world while instating the state of the world
// for the finally block.
FinallyContext finallyContext = topScope->finallyContext;
bool flipScopes = finallyContext.scopeContextStackSize != m_scopeContextStack.size();
bool flipSwitches = finallyContext.switchContextStackSize != m_switchContextStack.size();
bool flipForIns = finallyContext.forInContextStackSize != m_forInContextStack.size();
+ bool flipTries = finallyContext.tryContextStackSize != m_tryContextStack.size();
bool flipLabelScopes = finallyContext.labelScopesSize != m_labelScopes.size();
int topScopeIndex = -1;
int bottomScopeIndex = -1;
@@ -2177,6 +2200,19 @@
savedForInContextStack = m_forInContextStack;
m_forInContextStack.shrink(finallyContext.forInContextStackSize);
}
+ if (flipTries) {
+ while (m_tryContextStack.size() != finallyContext.tryContextStackSize) {
+ ASSERT(m_tryContextStack.size() > finallyContext.tryContextStackSize);
+ TryContext context = m_tryContextStack.last();
+ m_tryContextStack.removeLast();
+ TryRange range;
+ range.start = context.start;
+ range.end = beforeFinally;
+ range.tryData = context.tryData;
+ m_tryRanges.append(range);
+ poppedTryContexts.append(context);
+ }
+ }
if (flipLabelScopes) {
savedLabelScopes = m_labelScopes;
while (m_labelScopes.size() > finallyContext.labelScopesSize)
@@ -2190,6 +2226,8 @@
// Emit the finally block.
emitNode(finallyContext.finallyBlock);
+ RefPtr<Label> afterFinally = emitLabel(newLabel().get());
+
// Restore the state of the world.
if (flipScopes) {
m_scopeContextStack = savedScopeContextStack;
@@ -2200,6 +2238,14 @@
m_switchContextStack = savedSwitchContextStack;
if (flipForIns)
m_forInContextStack = savedForInContextStack;
+ if (flipTries) {
+ ASSERT(m_tryContextStack.size() == finallyContext.tryContextStackSize);
+ for (unsigned i = poppedTryContexts.size(); i--;) {
+ TryContext context = poppedTryContexts[i];
+ context.start = afterFinally;
+ m_tryContextStack.append(context);
+ }
+ }
if (flipLabelScopes)
m_labelScopes = savedLabelScopes;
m_finallyDepth = savedFinallyDepth;
@@ -2259,20 +2305,39 @@
return dst;
}
-RegisterID* BytecodeGenerator::emitCatch(RegisterID* targetRegister, Label* start, Label* end)
+TryData* BytecodeGenerator::pushTry(Label* start)
{
+ TryData tryData;
+ tryData.target = newLabel();
+ tryData.targetScopeDepth = UINT_MAX;
+ m_tryData.append(tryData);
+ TryData* result = &m_tryData.last();
+
+ TryContext tryContext;
+ tryContext.start = start;
+ tryContext.tryData = result;
+
+ m_tryContextStack.append(tryContext);
+
+ return result;
+}
+
+RegisterID* BytecodeGenerator::popTryAndEmitCatch(TryData* tryData, RegisterID* targetRegister, Label* end)
+{
m_usesExceptions = true;
-#if ENABLE(JIT)
-#if ENABLE(LLINT)
- HandlerInfo info = { start->bind(0, 0), end->bind(0, 0), instructions().size(), m_dynamicScopeDepth + m_baseScopeDepth, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(bitwise_cast<void*>(&llint_op_catch))) };
-#else
- HandlerInfo info = { start->bind(0, 0), end->bind(0, 0), instructions().size(), m_dynamicScopeDepth + m_baseScopeDepth, CodeLocationLabel() };
-#endif
-#else
- HandlerInfo info = { start->bind(0, 0), end->bind(0, 0), instructions().size(), m_dynamicScopeDepth + m_baseScopeDepth };
-#endif
+
+ ASSERT_UNUSED(tryData, m_tryContextStack.last().tryData == tryData);
+
+ TryRange tryRange;
+ tryRange.start = m_tryContextStack.last().start;
+ tryRange.end = end;
+ tryRange.tryData = m_tryContextStack.last().tryData;
+ m_tryRanges.append(tryRange);
+ m_tryContextStack.removeLast();
+
+ emitLabel(tryRange.tryData->target.get());
+ tryRange.tryData->targetScopeDepth = m_dynamicScopeDepth + m_baseScopeDepth;
- m_codeBlock->addExceptionHandler(info);
emitOpcode(op_catch);
instructions().append(targetRegister->index());
return targetRegister;
Modified: branches/safari-536.28-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (134302 => 134303)
--- branches/safari-536.28-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2012-11-12 22:21:18 UTC (rev 134302)
+++ branches/safari-536.28-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2012-11-12 22:21:47 UTC (rev 134303)
@@ -74,6 +74,7 @@
unsigned scopeContextStackSize;
unsigned switchContextStackSize;
unsigned forInContextStackSize;
+ unsigned tryContextStackSize;
unsigned labelScopesSize;
int finallyDepth;
int dynamicScopeDepth;
@@ -90,6 +91,22 @@
RefPtr<RegisterID> indexRegister;
RefPtr<RegisterID> propertyRegister;
};
+
+ struct TryData {
+ RefPtr<Label> target;
+ unsigned targetScopeDepth;
+ };
+
+ struct TryContext {
+ RefPtr<Label> start;
+ TryData* tryData;
+ };
+
+ struct TryRange {
+ RefPtr<Label> start;
+ RefPtr<Label> end;
+ TryData* tryData;
+ };
class BytecodeGenerator {
WTF_MAKE_FAST_ALLOCATED;
@@ -362,7 +379,11 @@
RegisterID* emitGetPropertyNames(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, Label* breakTarget);
RegisterID* emitNextPropertyName(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, RegisterID* iter, Label* target);
- RegisterID* emitCatch(RegisterID*, Label* start, Label* end);
+ // Start a try block. 'start' must have been emitted.
+ TryData* pushTry(Label* start);
+ // End a try block. 'end' must have been emitted.
+ RegisterID* popTryAndEmitCatch(TryData*, RegisterID* targetRegister, Label* end);
+
void emitThrow(RegisterID* exc)
{
m_usesExceptions = true;
@@ -490,8 +511,9 @@
}
RegisterID* emitInitLazyRegister(RegisterID*);
-
+ public:
Vector<Instruction>& instructions() { return m_instructions; }
+
SymbolTable& symbolTable() { return *m_symbolTable; }
bool shouldOptimizeLocals()
@@ -557,7 +579,11 @@
Vector<ControlFlowContext> m_scopeContextStack;
Vector<SwitchInfo> m_switchContextStack;
Vector<ForInContext> m_forInContextStack;
+ Vector<TryContext> m_tryContextStack;
+ Vector<TryRange> m_tryRanges;
+ SegmentedVector<TryData, 8> m_tryData;
+
int m_firstConstantIndex;
int m_nextConstantOffset;
unsigned m_globalConstantIndex;
Modified: branches/safari-536.28-branch/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (134302 => 134303)
--- branches/safari-536.28-branch/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2012-11-12 22:21:18 UTC (rev 134302)
+++ branches/safari-536.28-branch/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2012-11-12 22:21:47 UTC (rev 134303)
@@ -1973,11 +1973,15 @@
generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine());
+ ASSERT(m_catchBlock || m_finallyBlock);
+
RefPtr<Label> tryStartLabel = generator.newLabel();
+ generator.emitLabel(tryStartLabel.get());
+
if (m_finallyBlock)
generator.pushFinallyContext(m_finallyBlock);
+ TryData* tryData = generator.pushTry(tryStartLabel.get());
- generator.emitLabel(tryStartLabel.get());
generator.emitNode(dst, m_tryBlock);
if (m_catchBlock) {
@@ -1988,7 +1992,14 @@
// Uncaught exception path: the catch block.
RefPtr<Label> here = generator.emitLabel(generator.newLabel().get());
- RefPtr<RegisterID> exceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), here.get());
+ RefPtr<RegisterID> exceptionRegister = generator.popTryAndEmitCatch(tryData, generator.newTemporary(), here.get());
+
+ if (m_finallyBlock) {
+ // If the catch block throws an exception and we have a finally block, then the finally
+ // block should "catch" that exception.
+ tryData = generator.pushTry(here.get());
+ }
+
generator.emitPushNewScope(exceptionRegister.get(), m_exceptionIdent, exceptionRegister.get());
generator.emitNode(dst, m_catchBlock);
generator.emitPopScope();
@@ -1996,6 +2007,8 @@
}
if (m_finallyBlock) {
+ RefPtr<Label> preFinallyLabel = generator.emitLabel(generator.newLabel().get());
+
generator.popFinallyContext();
RefPtr<Label> finallyEndLabel = generator.newLabel();
@@ -2005,8 +2018,7 @@
generator.emitJump(finallyEndLabel.get());
// Uncaught exception path: invoke the finally block, then re-throw the exception.
- RefPtr<Label> here = generator.emitLabel(generator.newLabel().get());
- RefPtr<RegisterID> tempExceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), here.get());
+ RefPtr<RegisterID> tempExceptionRegister = generator.popTryAndEmitCatch(tryData, generator.newTemporary(), preFinallyLabel.get());
generator.emitNode(dst, m_finallyBlock);
generator.emitThrow(tempExceptionRegister.get());