<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[186996] trunk/Source/JavaScriptCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/186996">186996</a></dd>
<dt>Author</dt> <dd>saambarati1@gmail.com</dd>
<dt>Date</dt> <dd>2015-07-18 13:12:14 -0700 (Sat, 18 Jul 2015)</dd>
</dl>
<h3>Log Message</h3>
<pre>lexical scoping is broken with respect to "break" and "continue"
https://bugs.webkit.org/show_bug.cgi?id=147063
Reviewed by Filip Pizlo.
Bug #142944 which introduced "let" and lexical scoping
didn't properly hook into the bytecode generator's machinery
for calculating scope depth deltas for "break" and "continue". This
resulted in the bytecode generator popping an incorrect number
of scopes when lexical scopes were involved.
This patch fixes this problem and generalizes this machinery a bit.
This patch also renames old functions in a sensible way that is more
coherent in a world with lexical scoping.
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::newLabelScope):
(JSC::BytecodeGenerator::emitProfileType):
(JSC::BytecodeGenerator::pushLexicalScope):
(JSC::BytecodeGenerator::popLexicalScope):
(JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration):
(JSC::BytecodeGenerator::resolveType):
(JSC::BytecodeGenerator::emitResolveScope):
(JSC::BytecodeGenerator::emitGetFromScope):
(JSC::BytecodeGenerator::emitPutToScope):
(JSC::BytecodeGenerator::emitPushWithScope):
(JSC::BytecodeGenerator::emitGetParentScope):
(JSC::BytecodeGenerator::emitPopScope):
(JSC::BytecodeGenerator::emitPopWithOrCatchScope):
(JSC::BytecodeGenerator::emitPopScopes):
(JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler):
(JSC::BytecodeGenerator::localScopeDepth):
(JSC::BytecodeGenerator::labelScopeDepth):
(JSC::BytecodeGenerator::emitThrowReferenceError):
(JSC::BytecodeGenerator::emitPushFunctionNameScope):
(JSC::BytecodeGenerator::pushScopedControlFlowContext):
(JSC::BytecodeGenerator::popScopedControlFlowContext):
(JSC::BytecodeGenerator::emitPushCatchScope):
(JSC::BytecodeGenerator::currentScopeDepth): Deleted.
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::hasFinaliser):
(JSC::BytecodeGenerator::scopeDepth): Deleted.
* bytecompiler/NodesCodegen.cpp:
(JSC::ContinueNode::trivialTarget):
(JSC::BreakNode::trivialTarget):
(JSC::ReturnNode::emitBytecode):
(JSC::WithNode::emitBytecode):
(JSC::TryNode::emitBytecode):
* tests/stress/lexical-scoping-break-continue.js: Added.
(assert):
(.):</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorcpp">trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorh">trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecompilerNodesCodegencpp">trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstresslexicalscopingbreakcontinuejs">trunk/Source/JavaScriptCore/tests/stress/lexical-scoping-break-continue.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (186995 => 186996)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-07-18 19:48:59 UTC (rev 186995)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-07-18 20:12:14 UTC (rev 186996)
</span><span class="lines">@@ -1,3 +1,58 @@
</span><ins>+2015-07-18 Saam barati <saambarati1@gmail.com>
+
+ lexical scoping is broken with respect to "break" and "continue"
+ https://bugs.webkit.org/show_bug.cgi?id=147063
+
+ Reviewed by Filip Pizlo.
+
+ Bug #142944 which introduced "let" and lexical scoping
+ didn't properly hook into the bytecode generator's machinery
+ for calculating scope depth deltas for "break" and "continue". This
+ resulted in the bytecode generator popping an incorrect number
+ of scopes when lexical scopes were involved.
+
+ This patch fixes this problem and generalizes this machinery a bit.
+ This patch also renames old functions in a sensible way that is more
+ coherent in a world with lexical scoping.
+
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::BytecodeGenerator):
+ (JSC::BytecodeGenerator::newLabelScope):
+ (JSC::BytecodeGenerator::emitProfileType):
+ (JSC::BytecodeGenerator::pushLexicalScope):
+ (JSC::BytecodeGenerator::popLexicalScope):
+ (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration):
+ (JSC::BytecodeGenerator::resolveType):
+ (JSC::BytecodeGenerator::emitResolveScope):
+ (JSC::BytecodeGenerator::emitGetFromScope):
+ (JSC::BytecodeGenerator::emitPutToScope):
+ (JSC::BytecodeGenerator::emitPushWithScope):
+ (JSC::BytecodeGenerator::emitGetParentScope):
+ (JSC::BytecodeGenerator::emitPopScope):
+ (JSC::BytecodeGenerator::emitPopWithOrCatchScope):
+ (JSC::BytecodeGenerator::emitPopScopes):
+ (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler):
+ (JSC::BytecodeGenerator::localScopeDepth):
+ (JSC::BytecodeGenerator::labelScopeDepth):
+ (JSC::BytecodeGenerator::emitThrowReferenceError):
+ (JSC::BytecodeGenerator::emitPushFunctionNameScope):
+ (JSC::BytecodeGenerator::pushScopedControlFlowContext):
+ (JSC::BytecodeGenerator::popScopedControlFlowContext):
+ (JSC::BytecodeGenerator::emitPushCatchScope):
+ (JSC::BytecodeGenerator::currentScopeDepth): Deleted.
+ * bytecompiler/BytecodeGenerator.h:
+ (JSC::BytecodeGenerator::hasFinaliser):
+ (JSC::BytecodeGenerator::scopeDepth): Deleted.
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::ContinueNode::trivialTarget):
+ (JSC::BreakNode::trivialTarget):
+ (JSC::ReturnNode::emitBytecode):
+ (JSC::WithNode::emitBytecode):
+ (JSC::TryNode::emitBytecode):
+ * tests/stress/lexical-scoping-break-continue.js: Added.
+ (assert):
+ (.):
+
</ins><span class="cx"> 2015-07-17 Filip Pizlo <fpizlo@apple.com>
</span><span class="cx">
</span><span class="cx"> DFG should have some obvious mitigations against watching structures that are unprofitable to watch
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp (186995 => 186996)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp        2015-07-18 19:48:59 UTC (rev 186995)
+++ trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp        2015-07-18 20:12:14 UTC (rev 186996)
</span><span class="lines">@@ -522,6 +522,8 @@
</span><span class="cx"> instructions().append(0);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ if (m_lexicalEnvironmentRegister)
+ pushScopedControlFlowContext();
</ins><span class="cx"> m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(*m_vm, m_symbolTable), m_lexicalEnvironmentRegister, false, symbolTableConstantIndex });
</span><span class="cx"> m_TDZStack.append(std::make_pair(*parentScopeTDZVariables, false));
</span><span class="cx"> }
</span><span class="lines">@@ -627,7 +629,7 @@
</span><span class="cx"> m_labelScopes.removeLast();
</span><span class="cx">
</span><span class="cx"> // Allocate new label scope.
</span><del>- LabelScope scope(type, name, scopeDepth(), newLabel(), type == LabelScope::Loop ? newLabel() : PassRefPtr<Label>()); // Only loops have continue targets.
</del><ins>+ LabelScope scope(type, name, labelScopeDepth(), newLabel(), type == LabelScope::Loop ? newLabel() : PassRefPtr<Label>()); // Only loops have continue targets.
</ins><span class="cx"> m_labelScopes.append(scope);
</span><span class="cx"> return LabelScopePtr(m_labelScopes, m_labelScopes.size() - 1);
</span><span class="cx"> }
</span><span class="lines">@@ -1210,7 +1212,7 @@
</span><span class="cx"> // The format of this instruction is: op_profile_type regToProfile, TypeLocation*, flag, identifier?, resolveType?
</span><span class="cx"> emitOpcode(op_profile_type);
</span><span class="cx"> instructions().append(registerToProfile->index());
</span><del>- instructions().append(currentScopeDepth());
</del><ins>+ instructions().append(localScopeDepth());
</ins><span class="cx"> instructions().append(flag);
</span><span class="cx"> instructions().append(identifier ? addConstant(*identifier) : 0);
</span><span class="cx"> instructions().append(resolveType());
</span><span class="lines">@@ -1318,6 +1320,8 @@
</span><span class="cx"> instructions().append(addConstantValue(jsTDZValue())->index());
</span><span class="cx">
</span><span class="cx"> emitMove(scopeRegister(), newScope);
</span><ins>+
+ pushScopedControlFlowContext();
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> m_symbolTableStack.append(SymbolTableStackEntry{ symbolTable, newScope, false, symbolTableConstantIndex });
</span><span class="lines">@@ -1365,8 +1369,8 @@
</span><span class="cx">
</span><span class="cx"> if (hasCapturedVariables) {
</span><span class="cx"> RELEASE_ASSERT(stackEntry.m_scope);
</span><del>- RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), scopeRegister());
- emitMove(scopeRegister(), parentScope.get());
</del><ins>+ emitPopScope(scopeRegister(), stackEntry.m_scope);
+ popScopedControlFlowContext();
</ins><span class="cx"> stackEntry.m_scope->deref();
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -1423,7 +1427,7 @@
</span><span class="cx"> // as the previous scope because the loop body is compiled under
</span><span class="cx"> // the assumption that the scope's register index is constant even
</span><span class="cx"> // though the value in that register will change on each loop iteration.
</span><del>- RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), scopeRegister());
</del><ins>+ RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), loopScope);
</ins><span class="cx"> emitMove(scopeRegister(), parentScope.get());
</span><span class="cx">
</span><span class="cx"> emitOpcode(op_create_lexical_environment);
</span><span class="lines">@@ -1572,8 +1576,11 @@
</span><span class="cx"> // will start with this ResolveType and compute the least upper bound including intercepting scopes.
</span><span class="cx"> ResolveType BytecodeGenerator::resolveType()
</span><span class="cx"> {
</span><del>- if (m_localScopeDepth)
- return Dynamic;
</del><ins>+ for (unsigned i = m_symbolTableStack.size(); i--; ) {
+ if (m_symbolTableStack[i].m_isWithOrCatch)
+ return Dynamic;
+ }
+
</ins><span class="cx"> if (m_symbolTable && m_symbolTable->usesNonStrictEval())
</span><span class="cx"> return GlobalPropertyWithVarInjectionChecks;
</span><span class="cx"> return GlobalProperty;
</span><span class="lines">@@ -1632,7 +1639,7 @@
</span><span class="cx"> instructions().append(scopeRegister()->index());
</span><span class="cx"> instructions().append(addConstant(variable.ident()));
</span><span class="cx"> instructions().append(resolveType());
</span><del>- instructions().append(currentScopeDepth());
</del><ins>+ instructions().append(localScopeDepth());
</ins><span class="cx"> instructions().append(0);
</span><span class="cx"> return dst;
</span><span class="cx"> }
</span><span class="lines">@@ -1666,7 +1673,7 @@
</span><span class="cx"> instructions().append(scope->index());
</span><span class="cx"> instructions().append(addConstant(variable.ident()));
</span><span class="cx"> instructions().append(ResolveModeAndType(resolveMode, variable.offset().isScope() ? LocalClosureVar : resolveType()).operand());
</span><del>- instructions().append(currentScopeDepth());
</del><ins>+ instructions().append(localScopeDepth());
</ins><span class="cx"> instructions().append(variable.offset().isScope() ? variable.offset().scopeOffset().offset() : 0);
</span><span class="cx"> instructions().append(profile);
</span><span class="cx"> return dst;
</span><span class="lines">@@ -1706,7 +1713,7 @@
</span><span class="cx"> } else {
</span><span class="cx"> ASSERT(resolveType() != LocalClosureVar);
</span><span class="cx"> instructions().append(ResolveModeAndType(resolveMode, resolveType()).operand());
</span><del>- instructions().append(currentScopeDepth());
</del><ins>+ instructions().append(localScopeDepth());
</ins><span class="cx"> }
</span><span class="cx"> instructions().append(!!offset ? offset.offset() : 0);
</span><span class="cx"> return value;
</span><span class="lines">@@ -2474,10 +2481,7 @@
</span><span class="cx">
</span><span class="cx"> RegisterID* BytecodeGenerator::emitPushWithScope(RegisterID* dst, RegisterID* scope)
</span><span class="cx"> {
</span><del>- ControlFlowContext context;
- context.isFinallyBlock = false;
- m_scopeContextStack.append(context);
- m_localScopeDepth++;
</del><ins>+ pushScopedControlFlowContext();
</ins><span class="cx">
</span><span class="cx"> RegisterID* result = emitUnaryOp(op_push_with_scope, dst, scope);
</span><span class="cx"> m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(), nullptr, true, 0 });
</span><span class="lines">@@ -2492,16 +2496,16 @@
</span><span class="cx"> return dst;
</span><span class="cx"> }
</span><span class="cx">
</span><del>-void BytecodeGenerator::emitPopScope(RegisterID* srcDst)
</del><ins>+void BytecodeGenerator::emitPopScope(RegisterID* dst, RegisterID* scope)
</ins><span class="cx"> {
</span><del>- ASSERT(m_scopeContextStack.size());
- ASSERT(!m_scopeContextStack.last().isFinallyBlock);
</del><ins>+ RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), scope);
+ emitMove(dst, parentScope.get());
+}
</ins><span class="cx">
</span><del>- RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), srcDst);
- emitMove(srcDst, parentScope.get());
-
- m_scopeContextStack.removeLast();
- m_localScopeDepth--;
</del><ins>+void BytecodeGenerator::emitPopWithOrCatchScope(RegisterID* srcDst)
+{
+ emitPopScope(srcDst, srcDst);
+ popScopedControlFlowContext();
</ins><span class="cx"> SymbolTableStackEntry stackEntry = m_symbolTableStack.takeLast();
</span><span class="cx"> RELEASE_ASSERT(stackEntry.m_isWithOrCatch);
</span><span class="cx"> }
</span><span class="lines">@@ -2815,9 +2819,9 @@
</span><span class="cx">
</span><span class="cx"> void BytecodeGenerator::emitPopScopes(RegisterID* scope, int targetScopeDepth)
</span><span class="cx"> {
</span><del>- ASSERT(scopeDepth() - targetScopeDepth >= 0);
</del><ins>+ ASSERT(labelScopeDepth() - targetScopeDepth >= 0);
</ins><span class="cx">
</span><del>- size_t scopeDelta = scopeDepth() - targetScopeDepth;
</del><ins>+ size_t scopeDelta = labelScopeDepth() - targetScopeDepth;
</ins><span class="cx"> ASSERT(scopeDelta <= m_scopeContextStack.size());
</span><span class="cx"> if (!scopeDelta)
</span><span class="cx"> return;
</span><span class="lines">@@ -2876,16 +2880,10 @@
</span><span class="cx">
</span><span class="cx"> int BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler() const
</span><span class="cx"> {
</span><del>- int depth = m_localScopeDepth;
</del><ins>+ int depth = localScopeDepth();
</ins><span class="cx">
</span><del>- for (unsigned i = m_symbolTableStack.size(); i--; ) {
- RegisterID* scope = m_symbolTableStack[i].m_scope;
- if (scope)
- depth++;
- }
-
</del><span class="cx"> // Currently, we're maintaing compatibility with how things are done and letting the exception handling
</span><del>- // code take into consideration the base activation of the function. There is no reason we shouldn't
</del><ins>+ // code take into consideration the base activation of the function. There is no reason we shouldn't
</ins><span class="cx"> // be able to calculate the exact depth here and let the exception handler not worry if there is a base
</span><span class="cx"> // activation or not.
</span><span class="cx"> if (m_lexicalEnvironmentRegister)
</span><span class="lines">@@ -2895,18 +2893,16 @@
</span><span class="cx"> return depth;
</span><span class="cx"> }
</span><span class="cx">
</span><del>-int BytecodeGenerator::currentScopeDepth() const
</del><ins>+int BytecodeGenerator::localScopeDepth() const
</ins><span class="cx"> {
</span><del>- // This is the current number of JSScope descendents that would be allocated
- // in this function/program if this code were running.
- int depth = 0;
- for (unsigned i = m_symbolTableStack.size(); i--; ) {
- if (m_symbolTableStack[i].m_scope || m_symbolTableStack[i].m_isWithOrCatch)
- depth++;
- }
- return depth;
</del><ins>+ return m_localScopeDepth;
</ins><span class="cx"> }
</span><span class="cx">
</span><ins>+int BytecodeGenerator::labelScopeDepth() const
+{
+ return localScopeDepth() + m_finallyDepth;
+}
+
</ins><span class="cx"> void BytecodeGenerator::emitThrowReferenceError(const String& message)
</span><span class="cx"> {
</span><span class="cx"> emitOpcode(op_throw_static_error);
</span><span class="lines">@@ -2930,13 +2926,26 @@
</span><span class="cx"> instructions().append(JSNameScope::FunctionNameScope);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-void BytecodeGenerator::emitPushCatchScope(RegisterID* dst, const Identifier& property, RegisterID* value, unsigned attributes)
</del><ins>+void BytecodeGenerator::pushScopedControlFlowContext()
</ins><span class="cx"> {
</span><span class="cx"> ControlFlowContext context;
</span><span class="cx"> context.isFinallyBlock = false;
</span><span class="cx"> m_scopeContextStack.append(context);
</span><span class="cx"> m_localScopeDepth++;
</span><ins>+}
</ins><span class="cx">
</span><ins>+void BytecodeGenerator::popScopedControlFlowContext()
+{
+ ASSERT(m_scopeContextStack.size());
+ ASSERT(!m_scopeContextStack.last().isFinallyBlock);
+ m_scopeContextStack.removeLast();
+ m_localScopeDepth--;
+}
+
+void BytecodeGenerator::emitPushCatchScope(RegisterID* dst, const Identifier& property, RegisterID* value, unsigned attributes)
+{
+ pushScopedControlFlowContext();
+
</ins><span class="cx"> emitOpcode(op_push_name_scope);
</span><span class="cx"> instructions().append(dst->index());
</span><span class="cx"> instructions().append(value->index());
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h (186995 => 186996)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h        2015-07-18 19:48:59 UTC (rev 186995)
+++ trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h        2015-07-18 20:12:14 UTC (rev 186996)
</span><span class="lines">@@ -585,12 +585,12 @@
</span><span class="cx">
</span><span class="cx"> void emitGetScope();
</span><span class="cx"> RegisterID* emitPushWithScope(RegisterID* dst, RegisterID* scope);
</span><del>- void emitPopScope(RegisterID* srcDst);
</del><ins>+ void emitPopScope(RegisterID* dst, RegisterID* scope);
+ void emitPopWithOrCatchScope(RegisterID* srcDst);
</ins><span class="cx"> RegisterID* emitGetParentScope(RegisterID* dst, RegisterID* scope);
</span><span class="cx">
</span><span class="cx"> void emitDebugHook(DebugHookID, unsigned line, unsigned charOffset, unsigned lineStart);
</span><span class="cx">
</span><del>- int scopeDepth() { return m_localScopeDepth + m_finallyDepth; }
</del><span class="cx"> bool hasFinaliser() { return m_finallyDepth != 0; }
</span><span class="cx">
</span><span class="cx"> void pushFinallyContext(StatementNode* finallyBlock);
</span><span class="lines">@@ -624,6 +624,7 @@
</span><span class="cx"> void pushLexicalScope(VariableEnvironmentNode*, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult = nullptr);
</span><span class="cx"> void popLexicalScope(VariableEnvironmentNode*);
</span><span class="cx"> void prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode*, RegisterID* loopSymbolTable);
</span><ins>+ int labelScopeDepth() const;
</ins><span class="cx">
</span><span class="cx"> private:
</span><span class="cx"> void reclaimFreeRegisters();
</span><span class="lines">@@ -761,7 +762,9 @@
</span><span class="cx"> const CodeType m_codeType;
</span><span class="cx">
</span><span class="cx"> int calculateTargetScopeDepthForExceptionHandler() const;
</span><del>- int currentScopeDepth() const;
</del><ins>+ int localScopeDepth() const;
+ void pushScopedControlFlowContext();
+ void popScopedControlFlowContext();
</ins><span class="cx">
</span><span class="cx"> Vector<ControlFlowContext, 0, UnsafeVectorOverflow> m_scopeContextStack;
</span><span class="cx"> Vector<SwitchInfo> m_switchContextStack;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerNodesCodegencpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp (186995 => 186996)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp        2015-07-18 19:48:59 UTC (rev 186995)
+++ trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp        2015-07-18 20:12:14 UTC (rev 186996)
</span><span class="lines">@@ -2627,7 +2627,7 @@
</span><span class="cx"> LabelScopePtr scope = generator.continueTarget(m_ident);
</span><span class="cx"> ASSERT(scope);
</span><span class="cx">
</span><del>- if (generator.scopeDepth() != scope->scopeDepth())
</del><ins>+ if (generator.labelScopeDepth() != scope->scopeDepth())
</ins><span class="cx"> return 0;
</span><span class="cx">
</span><span class="cx"> return scope->continueTarget();
</span><span class="lines">@@ -2656,7 +2656,7 @@
</span><span class="cx"> LabelScopePtr scope = generator.breakTarget(m_ident);
</span><span class="cx"> ASSERT(scope);
</span><span class="cx">
</span><del>- if (generator.scopeDepth() != scope->scopeDepth())
</del><ins>+ if (generator.labelScopeDepth() != scope->scopeDepth())
</ins><span class="cx"> return 0;
</span><span class="cx">
</span><span class="cx"> return scope->breakTarget();
</span><span class="lines">@@ -2690,7 +2690,7 @@
</span><span class="cx"> generator.emitProfileType(returnRegister.get(), ProfileTypeBytecodeFunctionReturnStatement, nullptr);
</span><span class="cx"> generator.emitTypeProfilerExpressionInfo(divotStart(), divotEnd());
</span><span class="cx"> }
</span><del>- if (generator.scopeDepth()) {
</del><ins>+ if (generator.labelScopeDepth()) {
</ins><span class="cx"> returnRegister = generator.emitMove(generator.newTemporary(), returnRegister.get());
</span><span class="cx"> generator.emitPopScopes(generator.scopeRegister(), 0);
</span><span class="cx"> }
</span><span class="lines">@@ -2714,7 +2714,7 @@
</span><span class="cx"> generator.emitExpressionInfo(m_divot, m_divot - m_expressionLength, m_divot);
</span><span class="cx"> generator.emitPushWithScope(generator.scopeRegister(), scope.get());
</span><span class="cx"> generator.emitNode(dst, m_statement);
</span><del>- generator.emitPopScope(generator.scopeRegister());
</del><ins>+ generator.emitPopWithOrCatchScope(generator.scopeRegister());
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> // ------------------------------ CaseClauseNode --------------------------------
</span><span class="lines">@@ -2967,7 +2967,7 @@
</span><span class="cx"> generator.emitPushCatchScope(generator.scopeRegister(), m_thrownValueIdent, thrownValueRegister.get(), DontDelete);
</span><span class="cx"> generator.emitProfileControlFlow(m_tryBlock->endOffset() + 1);
</span><span class="cx"> generator.emitNode(dst, m_catchBlock);
</span><del>- generator.emitPopScope(generator.scopeRegister());
</del><ins>+ generator.emitPopWithOrCatchScope(generator.scopeRegister());
</ins><span class="cx"> generator.emitLabel(catchEndLabel.get());
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstresslexicalscopingbreakcontinuejs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/lexical-scoping-break-continue.js (0 => 186996)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/lexical-scoping-break-continue.js         (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/lexical-scoping-break-continue.js        2015-07-18 20:12:14 UTC (rev 186996)
</span><span class="lines">@@ -0,0 +1,216 @@
</span><ins>+function assert(b) {
+ if (!b)
+ throw new Error("bad assertion");
+}
+noInline(assert);
+
+;(function() {
+ function test1() {
+ let x = 20;
+ function foo() {
+ label: {
+ let y = 21;
+ let capY = function () { return y; }
+ assert(x === 20);
+ break label;
+ }
+ assert(x === 20);
+ }
+ foo();
+ }
+
+ function test2() {
+ let x = 20;
+ function capX() { return x; }
+ function foo() {
+ label1: {
+ label2: {
+ let y = 21;
+ let capY = function () { return y; }
+ break label2;
+ }
+ assert(x === 20);
+ }
+ assert(x === 20);
+
+ label1: {
+ label2: {
+ let y = 21;
+ let capY = function () { return y; }
+ assert(x === 20);
+ assert(y === 21);
+ break label1;
+ }
+ }
+ assert(x === 20);
+
+ label1: {
+ let y = 21;
+ let capY = function () { return y; }
+ label2: {
+ let y = 21;
+ let capY = function () { return y; }
+ assert(x === 20);
+ assert(y === 21);
+ break label1;
+ }
+ }
+ assert(x === 20);
+ }
+ foo()
+ }
+
+ function test3() {
+ let x = 20;
+ function capX() { return x; }
+ function foo() {
+ loop1: for (var i = 0; i++ < 1000; ) {
+ //assert(x === 20);
+ loop2: for (var j = 0; j++ < 1000; ) {
+ let y = 21;
+ let capY = function() { return y; }
+ assert(x === 20);
+ assert(y === 21);
+ continue loop1;
+ //break loop1;
+ }
+ }
+ assert(x === 20);
+ }
+ foo()
+ }
+
+ function test4() {
+ let x = 20;
+ function capX() { return x; }
+ function foo() {
+ loop1: for (var i = 0; i++ < 1000; ) {
+ loop2: for (var j = 0; j++ < 1000; ) {
+ let y = 21;
+ let capY = function() { return y; }
+ assert(x === 20);
+ assert(y === 21);
+ break loop1;
+ }
+ }
+ assert(x === 20);
+ }
+ foo()
+ }
+
+ function test5() {
+ let x = 20;
+ function capX() { return x; }
+ function foo() {
+ loop1: for (var i = 0; i++ < 1000; ) {
+ let y = 21;
+ let capY = function() { return y; }
+ loop2: for (var j = 0; j++ < 1000; ) {
+ let y = 21;
+ let capY = function() { return y; }
+ assert(x === 20);
+ assert(y === 21);
+ break loop1;
+ }
+ }
+ assert(x === 20);
+ }
+ foo()
+ }
+
+ function test6() {
+ let x = 20;
+ function capX() { return x; }
+ function foo() {
+ loop1: for (var i = 0; i++ < 1000; ) {
+ assert(x === 20);
+ let y = 21;
+ let capY = function() { return y; }
+ loop2: for (var j = 0; j++ < 1000; ) {
+ let y = 21;
+ let capY = function() { return y; }
+ assert(x === 20);
+ assert(y === 21);
+ try {
+ throw new Error();
+ } catch(e) {
+ } finally {
+ assert(x === 20);
+ continue loop1;
+ }
+ }
+ }
+ assert(x === 20);
+ }
+ foo()
+ }
+
+ function test7() {
+ let x = 20;
+ function capX() { return x; }
+ function foo() {
+ loop1: for (var i = 0; i++ < 1000; ) {
+ assert(x === 20);
+ let y = 21;
+ let capY = function() { return y; }
+ loop2: for (var j = 0; j++ < 1000; ) {
+ let y = 21;
+ let capY = function() { return y; }
+ assert(x === 20);
+ assert(y === 21);
+ try {
+ throw new Error();
+ } catch(e) {
+ continue loop1;
+ } finally {
+ let x = 40;
+ let capX = function() { return x; }
+ assert(x === 40);
+ }
+ }
+ }
+ assert(x === 20);
+ }
+ foo()
+ }
+
+ function test8() {
+ let x = 20;
+ function capX() { return x; }
+ function foo() {
+ loop1: for (var i = 0; i++ < 1000; ) {
+ assert(x === 20);
+ let y = 21;
+ let capY = function() { return y; }
+ loop2: for (var j = 0; j++ < 1000; ) {
+ let y = 21;
+ let capY = function() { return y; }
+ assert(x === 20);
+ assert(y === 21);
+ try {
+ throw new Error();
+ } catch(e) {
+ break loop1;
+ } finally {
+ let x = 40;
+ let capX = function() { return x; }
+ assert(x === 40);
+ }
+ }
+ }
+ assert(x === 20);
+ }
+ foo()
+ }
+
+ for (var i = 0; i < 1000; i++) {
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ test7();
+ test8();
+ }
+})();
</ins></span></pre>
</div>
</div>
</body>
</html>