<!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>[198989] trunk</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/198989">198989</a></dd>
<dt>Author</dt> <dd>sbarati@apple.com</dd>
<dt>Date</dt> <dd>2016-04-03 12:45:05 -0700 (Sun, 03 Apr 2016)</dd>
</dl>
<h3>Log Message</h3>
<pre>Implement Annex B.3.3 function hoisting rules for function code
https://bugs.webkit.org/show_bug.cgi?id=155672
Reviewed by Geoffrey Garen.
Source/JavaScriptCore:
The spec states that functions declared inside a function
inside a block scope are subject to the rules of Annex B.3.3:
https://tc39.github.io/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics
The rule states that functions declared in such blocks should
be local bindings of the block. If declaring the function's name
as a "var" in the function would not lead to a syntax error (i.e,
if we don't have a let/const/class variable with the same name)
and if we don't have a parameter with the same name, then we
implictly also declare the funcion name as a "var". When evaluating
the block statement we bind the hoisted "var" to be the value
of the local function binding.
There is one more thing we do for web compatibility. We allow
function declarations inside if/else statements that aren't
blocks. For such statements, we transform the code as if the
function were declared inside a block statement. For example:
``` function foo() { if (cond) function baz() { } }```
is transformed into:
``` function foo() { if (cond) { function baz() { } } }```
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack):
(JSC::BytecodeGenerator::initializeBlockScopedFunctions):
* bytecompiler/BytecodeGenerator.h:
* parser/Nodes.cpp:
(JSC::ScopeNode::ScopeNode):
(JSC::ProgramNode::ProgramNode):
(JSC::ModuleProgramNode::ModuleProgramNode):
(JSC::EvalNode::EvalNode):
(JSC::FunctionNode::FunctionNode):
* parser/Nodes.h:
(JSC::ScopeNode::hasCapturedVariables):
(JSC::ScopeNode::captures):
(JSC::ScopeNode::hasSloppyModeHoistedFunction):
(JSC::ScopeNode::varDeclarations):
(JSC::ProgramNode::startColumn):
(JSC::ProgramNode::endColumn):
(JSC::EvalNode::startColumn):
(JSC::EvalNode::endColumn):
(JSC::ModuleProgramNode::startColumn):
(JSC::ModuleProgramNode::endColumn):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::Parser):
(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::didFinishParsing):
(JSC::Parser<LexerType>::parseStatement):
(JSC::Parser<LexerType>::parseIfStatement):
* parser/Parser.h:
(JSC::Scope::declareVariable):
(JSC::Scope::declareFunction):
(JSC::Scope::addSloppyModeHoistableFunctionCandidate):
(JSC::Scope::appendFunction):
(JSC::Scope::declareParameter):
(JSC::Scope::mergeInnerArrowFunctionFeatures):
(JSC::Scope::getSloppyModeHoistedFunctions):
(JSC::Scope::getCapturedVars):
(JSC::ScopeRef::containingScope):
(JSC::ScopeRef::operator==):
(JSC::ScopeRef::operator!=):
(JSC::Parser::declareFunction):
(JSC::Parser::hasDeclaredVariable):
(JSC::Parser::isFunctionMetadataNode):
(JSC::Parser::DepthManager::DepthManager):
(JSC::Parser<LexerType>::parse):
* parser/VariableEnvironment.h:
(JSC::VariableEnvironmentEntry::isImported):
(JSC::VariableEnvironmentEntry::isImportedNamespace):
(JSC::VariableEnvironmentEntry::isFunction):
(JSC::VariableEnvironmentEntry::isParameter):
(JSC::VariableEnvironmentEntry::isSloppyModeHoistingCandidate):
(JSC::VariableEnvironmentEntry::setIsCaptured):
(JSC::VariableEnvironmentEntry::setIsConst):
(JSC::VariableEnvironmentEntry::setIsImported):
(JSC::VariableEnvironmentEntry::setIsImportedNamespace):
(JSC::VariableEnvironmentEntry::setIsFunction):
(JSC::VariableEnvironmentEntry::setIsParameter):
(JSC::VariableEnvironmentEntry::setIsSloppyModeHoistingCandidate):
(JSC::VariableEnvironmentEntry::clearIsVar):
* runtime/CodeCache.h:
(JSC::SourceCodeValue::SourceCodeValue):
* runtime/JSScope.cpp:
* runtime/JSScope.h:
* tests/es6.yaml:
* tests/stress/sloppy-mode-function-hoisting.js: Added.
(assert):
(test):
(falsey):
(truthy):
(test.):
(test.a):
(test.f):
(test.let.funcs.f):
(test.catch.f):
(test.foo):
(test.bar):
(test.switch.case.0):
(test.else.f):
(test.b):
(test.c):
(test.d):
(test.e):
(test.g):
(test.h):
(test.i):
(test.j):
(test.k):
(test.l):
(test.m):
(test.n):
(test.o):
(test.p):
(test.q):
(test.r):
(test.s):
(test.t):
(test.u):
(test.v):
(test.w):
(test.x):
(test.y):
(test.z):
(foo):
(bar):
(falsey.bar):
(baz):
(falsey.baz):
LayoutTests:
* js/kde/func-decl-expected.txt:
* js/kde/script-tests/func-decl.js:
* js/parser-syntax-check-expected.txt:
* js/script-tests/parser-syntax-check.js:
(valid):
(onlyValidGlobally):
(onlyInvalidGlobally):
(invalid):</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsjsfunctiondeclarationstatementexpectedtxt">trunk/LayoutTests/js/function-declaration-statement-expected.txt</a></li>
<li><a href="#trunkLayoutTestsjskdefuncdeclexpectedtxt">trunk/LayoutTests/js/kde/func-decl-expected.txt</a></li>
<li><a href="#trunkLayoutTestsjskdescripttestsfuncdecljs">trunk/LayoutTests/js/kde/script-tests/func-decl.js</a></li>
<li><a href="#trunkLayoutTestsjsparsersyntaxcheckexpectedtxt">trunk/LayoutTests/js/parser-syntax-check-expected.txt</a></li>
<li><a href="#trunkLayoutTestsjsscripttestsfunctiondeclarationstatementjs">trunk/LayoutTests/js/script-tests/function-declaration-statement.js</a></li>
<li><a href="#trunkLayoutTestsjsscripttestsparsersyntaxcheckjs">trunk/LayoutTests/js/script-tests/parser-syntax-check.js</a></li>
<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="#trunkSourceJavaScriptCoreparserNodescpp">trunk/Source/JavaScriptCore/parser/Nodes.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreparserNodesh">trunk/Source/JavaScriptCore/parser/Nodes.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreparserParsercpp">trunk/Source/JavaScriptCore/parser/Parser.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreparserParserh">trunk/Source/JavaScriptCore/parser/Parser.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreparserVariableEnvironmenth">trunk/Source/JavaScriptCore/parser/VariableEnvironment.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeCodeCacheh">trunk/Source/JavaScriptCore/runtime/CodeCache.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSScopecpp">trunk/Source/JavaScriptCore/runtime/JSScope.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSScopeh">trunk/Source/JavaScriptCore/runtime/JSScope.h</a></li>
<li><a href="#trunkSourceJavaScriptCoretestses6yaml">trunk/Source/JavaScriptCore/tests/es6.yaml</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstresssloppymodefunctionhoistingjs">trunk/Source/JavaScriptCore/tests/stress/sloppy-mode-function-hoisting.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/LayoutTests/ChangeLog        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2016-04-03 Saam barati <sbarati@apple.com>
+
+ Implement Annex B.3.3 function hoisting rules for function code
+ https://bugs.webkit.org/show_bug.cgi?id=155672
+
+ Reviewed by Geoffrey Garen.
+
+ * js/kde/func-decl-expected.txt:
+ * js/kde/script-tests/func-decl.js:
+ * js/parser-syntax-check-expected.txt:
+ * js/script-tests/parser-syntax-check.js:
+ (valid):
+ (onlyValidGlobally):
+ (onlyInvalidGlobally):
+ (invalid):
+
</ins><span class="cx"> 2016-04-03 David Kilzer <ddkilzer@apple.com>
</span><span class="cx">
</span><span class="cx"> REGRESSION (r198859): fast/scrolling/rtl-scrollbars-animation-property.html fails on non-Mac platforms
</span></span></pre></div>
<a id="trunkLayoutTestsjsfunctiondeclarationstatementexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/function-declaration-statement-expected.txt (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/function-declaration-statement-expected.txt        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/LayoutTests/js/function-declaration-statement-expected.txt        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -5,14 +5,6 @@
</span><span class="cx">
</span><span class="cx"> PASS ifTest() is true
</span><span class="cx"> PASS ifElseTest() is true
</span><del>-PASS doWhileTest() is true
-PASS whileTest() is true
-PASS forTest() is true
-PASS forVarTest() is true
-PASS forInTest() is true
-PASS forInVarTest() is true
-PASS forInVarInitTest() is true
-PASS withTest() is true
</del><span class="cx"> PASS labelTest() is true
</span><span class="cx"> PASS successfullyParsed is true
</span><span class="cx">
</span></span></pre></div>
<a id="trunkLayoutTestsjskdefuncdeclexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/kde/func-decl-expected.txt (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/kde/func-decl-expected.txt        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/LayoutTests/js/kde/func-decl-expected.txt        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -3,8 +3,8 @@
</span><span class="cx"> On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
</span><span class="cx">
</span><span class="cx">
</span><del>-PASS Function declaration takes effect at entry
-PASS Decl not yet overwritten
</del><ins>+PASS Function declaration takes effect at declaration block
+PASS Decl already overwritten
</ins><span class="cx"> PASS After assign (0)
</span><span class="cx"> PASS function decls have no execution content
</span><span class="cx"> PASS After assign #2 (0)
</span></span></pre></div>
<a id="trunkLayoutTestsjskdescripttestsfuncdecljs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/kde/script-tests/func-decl.js (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/kde/script-tests/func-decl.js        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/LayoutTests/js/kde/script-tests/func-decl.js        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -17,17 +17,14 @@
</span><span class="cx">
</span><span class="cx"> function test() {
</span><span class="cx"> try {
</span><del>- shouldBeOfType("Function declaration takes effect at entry", f, "function");
</del><ins>+ shouldBeOfType("Function declaration takes effect at declaration block", f, "undefined");
</ins><span class="cx"> }
</span><span class="cx"> catch (e) {
</span><span class="cx"> testFailed("Scoping very broken!");
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> for (var i = 0; i < 3; ++i) {
</span><del>- if (i == 0)
- shouldBeOfType("Decl not yet overwritten", f, 'function');
- else
- shouldBeOfType("Decl already overwritten", f, 'number');
</del><ins>+ shouldBeOfType("Decl already overwritten", f, 'function');
</ins><span class="cx">
</span><span class="cx"> f = 3;
</span><span class="cx"> shouldBeVal("After assign ("+i+")", f, 3);
</span></span></pre></div>
<a id="trunkLayoutTestsjsparsersyntaxcheckexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/parser-syntax-check-expected.txt (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/parser-syntax-check-expected.txt        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/LayoutTests/js/parser-syntax-check-expected.txt        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -302,8 +302,8 @@
</span><span class="cx"> PASS Invalid: "function f() { while if (a) ; }"
</span><span class="cx"> PASS Valid: "if (a) function f() {} else function g() {}"
</span><span class="cx"> PASS Valid: "function f() { if (a) function f() {} else function g() {} }"
</span><del>-PASS Valid: "if (a()) while(0) function f() {} else function g() {}" with TypeError
-PASS Valid: "function f() { if (a()) while(0) function f() {} else function g() {} }"
</del><ins>+PASS Invalid: "if (a()) while(0) function f() {} else function g() {}"
+PASS Invalid: "function f() { if (a()) while(0) function f() {} else function g() {} }"
</ins><span class="cx"> PASS Invalid: "if (a()) function f() { else function g() }"
</span><span class="cx"> PASS Invalid: "function f() { if (a()) function f() { else function g() } }"
</span><span class="cx"> PASS Invalid: "if (a) if (b) ; else function f {}"
</span><span class="lines">@@ -676,16 +676,50 @@
</span><span class="cx"> PASS Invalid: "function f() { 'use strict'; function f1(a) {}; let f1; }"
</span><span class="cx"> PASS Invalid: "let f1; function f1(a) {};"
</span><span class="cx"> PASS Invalid: "function f() { let f1; function f1(a) {}; }"
</span><del>-PASS Invalid: "let f1; { function f1(a) {}; }"
-PASS Invalid: "function f() { let f1; { function f1(a) {}; } }"
-PASS Invalid: "{ function f1(a) {}; } let f1;"
-PASS Invalid: "function f() { { function f1(a) {}; } let f1; }"
-PASS Valid: "{ let f1; function f1(a) {}; }"
-PASS Valid: "function f() { { let f1; function f1(a) {}; } }"
-PASS Valid: "{ function f1(a) {}; let f1; }"
-PASS Valid: "function f() { { function f1(a) {}; let f1; } }"
</del><ins>+PASS Valid: "function foo() { let f1; { function f1(a) {}; } }"
+PASS Valid: "function f() { function foo() { let f1; { function f1(a) {}; } } }"
+PASS Valid: "function foo() { { function f1(a) {}; } let f1; }"
+PASS Valid: "function f() { function foo() { { function f1(a) {}; } let f1; } }"
+PASS Valid: "function foo() { { function foo() { }; function foo() { } } }"
+PASS Valid: "function f() { function foo() { { function foo() { }; function foo() { } } } }"
+PASS Invalid: "function foo() { 'use strict'; { function foo() { }; function foo() { } } }"
+PASS Invalid: "function f() { function foo() { 'use strict'; { function foo() { }; function foo() { } } } }"
+PASS Invalid: "function foo() { let f1; function f1(a) {}; }"
+PASS Invalid: "function f() { function foo() { let f1; function f1(a) {}; } }"
+PASS Invalid: "let f1; function f1(a) {};"
+PASS Invalid: "function f() { let f1; function f1(a) {}; }"
+PASS Valid: "{ function f1(a) {}; let f1; }"
+PASS Invalid: "function f() { { function f1(a) {}; let f1; } }"
+PASS Valid: "{ function f1(a) {}; const f1 = 25; }"
+PASS Invalid: "function f() { { function f1(a) {}; const f1 = 25; } }"
+PASS Valid: "{ function f1(a) {}; class f1{}; }"
+PASS Invalid: "function f() { { function f1(a) {}; class f1{}; } }"
+PASS Invalid: "function foo() { { let bar; function bar() { } } }"
+PASS Invalid: "function f() { function foo() { { let bar; function bar() { } } } }"
+PASS Invalid: "function foo() { { function bar() { }; let bar; } }"
+PASS Invalid: "function f() { function foo() { { function bar() { }; let bar; } } }"
+PASS Invalid: "function foo() { { const bar; function bar() { } } }"
+PASS Invalid: "function f() { function foo() { { const bar; function bar() { } } } }"
+PASS Invalid: "function foo() { { function bar() { }; const bar; } }"
+PASS Invalid: "function f() { function foo() { { function bar() { }; const bar; } } }"
+PASS Invalid: "function foo() { { class bar{}; function bar() { } } }"
+PASS Invalid: "function f() { function foo() { { class bar{}; function bar() { } } } }"
+PASS Invalid: "function foo() { { function bar() { }; class bar{}; } }"
+PASS Invalid: "function f() { function foo() { { function bar() { }; class bar{}; } } }"
</ins><span class="cx"> PASS Valid: "switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }"
</span><span class="cx"> PASS Valid: "function f() { switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; } }"
</span><ins>+PASS Valid: "switch('foo') { case 1: let foo; function foo() {}; break; case 2: function foo() {}; break; }"
+PASS Invalid: "function f() { switch('foo') { case 1: let foo; function foo() {}; break; case 2: function foo() {}; break; } }"
+PASS Valid: "switch('foo') { case 1: function foo() {}; let foo; break; case 2: function foo() {}; break; }"
+PASS Invalid: "function f() { switch('foo') { case 1: function foo() {}; let foo; break; case 2: function foo() {}; break; } }"
+PASS Valid: "switch('foo') { case 1: function foo() {}; const foo = 25; break; case 2: function foo() {}; break; }"
+PASS Invalid: "function f() { switch('foo') { case 1: function foo() {}; const foo = 25; break; case 2: function foo() {}; break; } }"
+PASS Valid: "switch('foo') { case 1: function foo() {}; class foo {} ; break; case 2: function foo() {}; break; }"
+PASS Invalid: "function f() { switch('foo') { case 1: function foo() {}; class foo {} ; break; case 2: function foo() {}; break; } }"
+PASS Valid: "switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: let foo; }"
+PASS Invalid: "function f() { switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: let foo; } }"
+PASS Valid: "function foo() { switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: { let foo; } } }"
+PASS Valid: "function f() { function foo() { switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: { let foo; } } } }"
</ins><span class="cx"> PASS Invalid: "'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }"
</span><span class="cx"> PASS Invalid: "function f() { 'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; } }"
</span><span class="cx"> PASS Invalid: "'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: let foo; break; }"
</span><span class="lines">@@ -700,6 +734,74 @@
</span><span class="cx"> PASS Invalid: "function f() { 'use strict'; if (true) function foo() { }; }"
</span><span class="cx"> PASS Valid: "if (true) function foo() { }; "
</span><span class="cx"> PASS Valid: "function f() { if (true) function foo() { }; }"
</span><ins>+PASS Invalid: " let foo; if (true) function foo() { };"
+PASS Valid: "function f() { let foo; if (true) function foo() { }; }"
+PASS Valid: "function baz() { let foo; if (true) function foo() { }; }"
+PASS Valid: "function f() { function baz() { let foo; if (true) function foo() { }; } }"
+PASS Invalid: "if (true) function foo() { }; let foo;"
+PASS Valid: "function f() { if (true) function foo() { }; let foo; }"
+PASS Invalid: "{ if (true) function foo() { }; } let foo;"
+PASS Valid: "function f() { { if (true) function foo() { }; } let foo; }"
+PASS Invalid: "let foo; while (false) function foo() { }; "
+PASS Invalid: "function f() { let foo; while (false) function foo() { }; }"
+PASS Invalid: "let foo; { while (false) function foo() { }; } "
+PASS Invalid: "function f() { let foo; { while (false) function foo() { }; } }"
+PASS Invalid: "while (false) function foo() { }; let foo;"
+PASS Invalid: "function f() { while (false) function foo() { }; let foo; }"
+PASS Invalid: "let foo; while (false) label: function foo() { }; "
+PASS Invalid: "function f() { let foo; while (false) label: function foo() { }; }"
+PASS Invalid: "while (false) label: function foo() { }; let foo;"
+PASS Invalid: "function f() { while (false) label: function foo() { }; let foo; }"
+PASS Invalid: "'use strict'; while (false) function foo() { }; "
+PASS Invalid: "function f() { 'use strict'; while (false) function foo() { }; }"
+PASS Invalid: "'use strict'; if (false) function foo() { }; "
+PASS Invalid: "function f() { 'use strict'; if (false) function foo() { }; }"
+PASS Invalid: "'use strict'; do function foo() { } while (false); "
+PASS Invalid: "function f() { 'use strict'; do function foo() { } while (false); }"
+PASS Invalid: "while (false) function foo() { }; "
+PASS Invalid: "function f() { while (false) function foo() { }; }"
+PASS Valid: "if (false) function foo() { }; "
+PASS Valid: "function f() { if (false) function foo() { }; }"
+PASS Invalid: "do function foo() { } while (false); "
+PASS Invalid: "function f() { do function foo() { } while (false); }"
+PASS Invalid: "if (cond) label: function foo() { }"
+PASS Invalid: "function f() { if (cond) label: function foo() { } }"
+PASS Invalid: "while (true) { while (true) function bar() { } }"
+PASS Invalid: "function f() { while (true) { while (true) function bar() { } } }"
+PASS Invalid: "with ({}) function bar() { }"
+PASS Invalid: "function f() { with ({}) function bar() { } }"
+PASS Valid: "function bar() { label: function baz() { } }"
+PASS Valid: "function f() { function bar() { label: function baz() { } } }"
+PASS Valid: "function bar() { let: function baz() { } }"
+PASS Valid: "function f() { function bar() { let: function baz() { } } }"
+PASS Invalid: "function bar() { 'use strict'; let: function baz() { } }"
+PASS Invalid: "function f() { function bar() { 'use strict'; let: function baz() { } } }"
+PASS Valid: "function bar() { yield: function baz() { } }"
+PASS Valid: "function f() { function bar() { yield: function baz() { } } }"
+PASS Valid: "function bar() { label: label2: function baz() { } }"
+PASS Valid: "function f() { function bar() { label: label2: function baz() { } } }"
+PASS Valid: "function bar() { label: label2: label3: function baz() { } }"
+PASS Valid: "function f() { function bar() { label: label2: label3: function baz() { } } }"
+PASS Invalid: "function bar() { label: label2: label weird: function baz() { } }"
+PASS Invalid: "function f() { function bar() { label: label2: label weird: function baz() { } } }"
+PASS Valid: "function bar() { label: label2: label3: function baz() { } }"
+PASS Valid: "function f() { function bar() { label: label2: label3: function baz() { } } }"
+PASS Invalid: "function bar() { 'use strict'; label: label2: label 3: function baz() { } }"
+PASS Invalid: "function f() { function bar() { 'use strict'; label: label2: label 3: function baz() { } } }"
+PASS Invalid: "function bar() { if (cond) label: function foo() { } }"
+PASS Invalid: "function f() { function bar() { if (cond) label: function foo() { } } }"
+PASS Invalid: "function bar() { while (cond) label: function foo() { } }"
+PASS Invalid: "function f() { function bar() { while (cond) label: function foo() { } } }"
+PASS Valid: "label: function foo() { }"
+PASS Valid: "function f() { label: function foo() { } }"
+PASS Valid: "let: function foo() { }"
+PASS Valid: "function f() { let: function foo() { } }"
+PASS Valid: "yield: function foo() { }"
+PASS Valid: "function f() { yield: function foo() { } }"
+PASS Valid: "yield: let: function foo() { }"
+PASS Valid: "function f() { yield: let: function foo() { } }"
+PASS Invalid: "'use strict'; yield: let: function foo() { }"
+PASS Invalid: "function f() { 'use strict'; yield: let: function foo() { } }"
</ins><span class="cx"> PASS Valid: "var str = "'use strict'; function f1(a) { function f2(b) { return b; } return f2(a); } return f1(arguments[0]);"; var foo = new Function(str); foo(5);"
</span><span class="cx"> PASS Valid: "function f() { var str = "'use strict'; function f1(a) { function f2(b) { return b; } return f2(a); } return f1(arguments[0]);"; var foo = new Function(str); foo(5); }"
</span><span class="cx"> PASS Valid: "var str = "'use strict'; function f1(a) { function f2(b) { function f3(c) { return c; } return f3(b); } return f2(a); } return f1(arguments[0]);"; var foo = new Function(str); foo(5);"
</span></span></pre></div>
<a id="trunkLayoutTestsjsscripttestsfunctiondeclarationstatementjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/script-tests/function-declaration-statement.js (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/script-tests/function-declaration-statement.js        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/LayoutTests/js/script-tests/function-declaration-statement.js        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -35,117 +35,6 @@
</span><span class="cx">
</span><span class="cx"> shouldBeTrue("ifElseTest()");
</span><span class="cx">
</span><del>-function doWhileTest()
-{
- var i = 0;
- do
- function f()
- {
- return true;
- }
- while (i++ < 10)
-
- return f();
-}
-
-shouldBeTrue("doWhileTest()");
-
-function whileTest()
-{
- var i = 0;
- while (i++ < 10)
- function f()
- {
- return true;
- }
-
- return f();
-}
-
-shouldBeTrue("whileTest()");
-
-function forTest()
-{
- var i;
- for (i = 0; i < 10; ++i)
- function f()
- {
- return true;
- }
-
- return f();
-}
-
-shouldBeTrue("forTest()");
-
-function forVarTest()
-{
- for (var i = 0; i < 10; ++i)
- function f()
- {
- return true;
- }
-
- return f();
-}
-
-shouldBeTrue("forVarTest()");
-
-function forInTest()
-{
- var a;
- for (a in { field: false })
- function f()
- {
- return true;
- }
-
- return f();
-}
-
-shouldBeTrue("forInTest()");
-
-function forInVarTest()
-{
- var a;
- for (var a in { field: false })
- function f()
- {
- return true;
- }
-
- return f();
-}
-
-shouldBeTrue("forInVarTest()");
-
-function forInVarInitTest()
-{
- var a;
- for (var a in { field: false })
- function f()
- {
- return true;
- }
-
- return f();
-}
-
-shouldBeTrue("forInVarInitTest()");
-
-function withTest()
-{
- with ({ })
- function f()
- {
- return true;
- }
-
- return f();
-}
-
-shouldBeTrue("withTest()");
-
</del><span class="cx"> function labelTest()
</span><span class="cx"> {
</span><span class="cx"> label:
</span></span></pre></div>
<a id="trunkLayoutTestsjsscripttestsparsersyntaxcheckjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/script-tests/parser-syntax-check.js (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/script-tests/parser-syntax-check.js        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/LayoutTests/js/script-tests/parser-syntax-check.js        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -43,6 +43,18 @@
</span><span class="cx"> runTest("function f() { " + _a + " }", false);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+function onlyValidGlobally(_a)
+{
+ runTest(_a, false);
+ runTest("function f() { " + _a + " }", true);
+}
+
+function onlyInvalidGlobally(_a)
+{
+ runTest(_a, true);
+ runTest("function f() { " + _a + " }", false);
+}
+
</ins><span class="cx"> function invalid(_a)
</span><span class="cx"> {
</span><span class="cx"> // Test both the grammar and the syntax checker
</span><span class="lines">@@ -226,7 +238,7 @@
</span><span class="cx"> invalid("do if (a) while (false) else debugger");
</span><span class="cx"> invalid("while if (a) ;");
</span><span class="cx"> valid ("if (a) function f() {} else function g() {}");
</span><del>-valid ("if (a()) while(0) function f() {} else function g() {}");
</del><ins>+invalid("if (a()) while(0) function f() {} else function g() {}");
</ins><span class="cx"> invalid("if (a()) function f() { else function g() }");
</span><span class="cx"> invalid("if (a) if (b) ; else function f {}");
</span><span class="cx"> invalid("if (a) if (b) ; else function (){}");
</span><span class="lines">@@ -428,11 +440,28 @@
</span><span class="cx"> invalid("'use strict'; let f1; function f1(a) {};")
</span><span class="cx"> invalid("'use strict'; function f1(a) {}; let f1; ")
</span><span class="cx"> invalid("let f1; function f1(a) {};")
</span><del>-invalid("let f1; { function f1(a) {}; }")
-invalid("{ function f1(a) {}; } let f1;")
-valid("{ let f1; function f1(a) {}; }")
-valid("{ function f1(a) {}; let f1; }")
</del><ins>+valid("function foo() { let f1; { function f1(a) {}; } }")
+valid("function foo() { { function f1(a) {}; } let f1; }")
+valid("function foo() { { function foo() { }; function foo() { } } }")
+invalid("function foo() { 'use strict'; { function foo() { }; function foo() { } } }")
+invalid("function foo() { let f1; function f1(a) {}; }")
+invalid("let f1; function f1(a) {};")
+onlyValidGlobally("{ function f1(a) {}; let f1; }")
+onlyValidGlobally("{ function f1(a) {}; const f1 = 25; }")
+onlyValidGlobally("{ function f1(a) {}; class f1{}; }")
+invalid("function foo() { { let bar; function bar() { } } }")
+invalid("function foo() { { function bar() { }; let bar; } }")
+invalid("function foo() { { const bar; function bar() { } } }")
+invalid("function foo() { { function bar() { }; const bar; } }")
+invalid("function foo() { { class bar{}; function bar() { } } }")
+invalid("function foo() { { function bar() { }; class bar{}; } }")
</ins><span class="cx"> valid("switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }")
</span><ins>+onlyValidGlobally("switch('foo') { case 1: let foo; function foo() {}; break; case 2: function foo() {}; break; }")
+onlyValidGlobally("switch('foo') { case 1: function foo() {}; let foo; break; case 2: function foo() {}; break; }")
+onlyValidGlobally("switch('foo') { case 1: function foo() {}; const foo = 25; break; case 2: function foo() {}; break; }")
+onlyValidGlobally("switch('foo') { case 1: function foo() {}; class foo {} ; break; case 2: function foo() {}; break; }")
+onlyValidGlobally("switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: let foo; }")
+valid("function foo() { switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: { let foo; } } }")
</ins><span class="cx"> invalid("'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }");
</span><span class="cx"> invalid("'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: let foo; break; }");
</span><span class="cx"> invalid("'use strict'; switch('foo') { case 1: let foo; break; case 2: function foo() {}; break; }");
</span><span class="lines">@@ -440,6 +469,40 @@
</span><span class="cx"> valid("'use strict'; switch('foo') { case 1: { function foo() { }; break; } case 2: function foo() {}; break; }");
</span><span class="cx"> invalid("'use strict'; if (true) function foo() { }; ");
</span><span class="cx"> valid("if (true) function foo() { }; ");
</span><ins>+onlyInvalidGlobally(" let foo; if (true) function foo() { };");
+valid("function baz() { let foo; if (true) function foo() { }; }");
+onlyInvalidGlobally("if (true) function foo() { }; let foo;");
+onlyInvalidGlobally("{ if (true) function foo() { }; } let foo;");
+invalid("let foo; while (false) function foo() { }; ");
+invalid("let foo; { while (false) function foo() { }; } ");
+invalid("while (false) function foo() { }; let foo;");
+invalid("let foo; while (false) label: function foo() { }; ");
+invalid("while (false) label: function foo() { }; let foo;");
+invalid("'use strict'; while (false) function foo() { }; ");
+invalid("'use strict'; if (false) function foo() { }; ");
+invalid("'use strict'; do function foo() { } while (false); ");
+invalid("while (false) function foo() { }; ");
+valid("if (false) function foo() { }; ");
+invalid("do function foo() { } while (false); ");
+invalid("if (cond) label: function foo() { }");
+invalid("while (true) { while (true) function bar() { } }");
+invalid("with ({}) function bar() { }");
+valid("function bar() { label: function baz() { } }");
+valid("function bar() { let: function baz() { } }");
+invalid("function bar() { 'use strict'; let: function baz() { } }");
+valid("function bar() { yield: function baz() { } }");
+valid("function bar() { label: label2: function baz() { } }");
+valid("function bar() { label: label2: label3: function baz() { } }");
+invalid("function bar() { label: label2: label weird: function baz() { } }");
+valid("function bar() { label: label2: label3: function baz() { } }");
+invalid("function bar() { 'use strict'; label: label2: label 3: function baz() { } }");
+invalid("function bar() { if (cond) label: function foo() { } }");
+invalid("function bar() { while (cond) label: function foo() { } }");
+valid("label: function foo() { }");
+valid("let: function foo() { }");
+valid("yield: function foo() { }");
+valid("yield: let: function foo() { }");
+invalid("'use strict'; yield: let: function foo() { }");
</ins><span class="cx">
</span><span class="cx"> valid("var str = \"'use strict'; function f1(a) { function f2(b) { return b; } return f2(a); } return f1(arguments[0]);\"; var foo = new Function(str); foo(5);")
</span><span class="cx"> valid("var str = \"'use strict'; function f1(a) { function f2(b) { function f3(c) { return c; } return f3(b); } return f2(a); } return f1(arguments[0]);\"; var foo = new Function(str); foo(5);")
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -1,3 +1,138 @@
</span><ins>+2016-04-03 Saam barati <sbarati@apple.com>
+
+ Implement Annex B.3.3 function hoisting rules for function code
+ https://bugs.webkit.org/show_bug.cgi?id=155672
+
+ Reviewed by Geoffrey Garen.
+
+ The spec states that functions declared inside a function
+ inside a block scope are subject to the rules of Annex B.3.3:
+ https://tc39.github.io/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics
+
+ The rule states that functions declared in such blocks should
+ be local bindings of the block. If declaring the function's name
+ as a "var" in the function would not lead to a syntax error (i.e,
+ if we don't have a let/const/class variable with the same name)
+ and if we don't have a parameter with the same name, then we
+ implictly also declare the funcion name as a "var". When evaluating
+ the block statement we bind the hoisted "var" to be the value
+ of the local function binding.
+
+ There is one more thing we do for web compatibility. We allow
+ function declarations inside if/else statements that aren't
+ blocks. For such statements, we transform the code as if the
+ function were declared inside a block statement. For example:
+ ``` function foo() { if (cond) function baz() { } }```
+ is transformed into:
+ ``` function foo() { if (cond) { function baz() { } } }```
+
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack):
+ (JSC::BytecodeGenerator::initializeBlockScopedFunctions):
+ * bytecompiler/BytecodeGenerator.h:
+ * parser/Nodes.cpp:
+ (JSC::ScopeNode::ScopeNode):
+ (JSC::ProgramNode::ProgramNode):
+ (JSC::ModuleProgramNode::ModuleProgramNode):
+ (JSC::EvalNode::EvalNode):
+ (JSC::FunctionNode::FunctionNode):
+ * parser/Nodes.h:
+ (JSC::ScopeNode::hasCapturedVariables):
+ (JSC::ScopeNode::captures):
+ (JSC::ScopeNode::hasSloppyModeHoistedFunction):
+ (JSC::ScopeNode::varDeclarations):
+ (JSC::ProgramNode::startColumn):
+ (JSC::ProgramNode::endColumn):
+ (JSC::EvalNode::startColumn):
+ (JSC::EvalNode::endColumn):
+ (JSC::ModuleProgramNode::startColumn):
+ (JSC::ModuleProgramNode::endColumn):
+ * parser/Parser.cpp:
+ (JSC::Parser<LexerType>::Parser):
+ (JSC::Parser<LexerType>::parseInner):
+ (JSC::Parser<LexerType>::didFinishParsing):
+ (JSC::Parser<LexerType>::parseStatement):
+ (JSC::Parser<LexerType>::parseIfStatement):
+ * parser/Parser.h:
+ (JSC::Scope::declareVariable):
+ (JSC::Scope::declareFunction):
+ (JSC::Scope::addSloppyModeHoistableFunctionCandidate):
+ (JSC::Scope::appendFunction):
+ (JSC::Scope::declareParameter):
+ (JSC::Scope::mergeInnerArrowFunctionFeatures):
+ (JSC::Scope::getSloppyModeHoistedFunctions):
+ (JSC::Scope::getCapturedVars):
+ (JSC::ScopeRef::containingScope):
+ (JSC::ScopeRef::operator==):
+ (JSC::ScopeRef::operator!=):
+ (JSC::Parser::declareFunction):
+ (JSC::Parser::hasDeclaredVariable):
+ (JSC::Parser::isFunctionMetadataNode):
+ (JSC::Parser::DepthManager::DepthManager):
+ (JSC::Parser<LexerType>::parse):
+ * parser/VariableEnvironment.h:
+ (JSC::VariableEnvironmentEntry::isImported):
+ (JSC::VariableEnvironmentEntry::isImportedNamespace):
+ (JSC::VariableEnvironmentEntry::isFunction):
+ (JSC::VariableEnvironmentEntry::isParameter):
+ (JSC::VariableEnvironmentEntry::isSloppyModeHoistingCandidate):
+ (JSC::VariableEnvironmentEntry::setIsCaptured):
+ (JSC::VariableEnvironmentEntry::setIsConst):
+ (JSC::VariableEnvironmentEntry::setIsImported):
+ (JSC::VariableEnvironmentEntry::setIsImportedNamespace):
+ (JSC::VariableEnvironmentEntry::setIsFunction):
+ (JSC::VariableEnvironmentEntry::setIsParameter):
+ (JSC::VariableEnvironmentEntry::setIsSloppyModeHoistingCandidate):
+ (JSC::VariableEnvironmentEntry::clearIsVar):
+ * runtime/CodeCache.h:
+ (JSC::SourceCodeValue::SourceCodeValue):
+ * runtime/JSScope.cpp:
+ * runtime/JSScope.h:
+ * tests/es6.yaml:
+ * tests/stress/sloppy-mode-function-hoisting.js: Added.
+ (assert):
+ (test):
+ (falsey):
+ (truthy):
+ (test.):
+ (test.a):
+ (test.f):
+ (test.let.funcs.f):
+ (test.catch.f):
+ (test.foo):
+ (test.bar):
+ (test.switch.case.0):
+ (test.else.f):
+ (test.b):
+ (test.c):
+ (test.d):
+ (test.e):
+ (test.g):
+ (test.h):
+ (test.i):
+ (test.j):
+ (test.k):
+ (test.l):
+ (test.m):
+ (test.n):
+ (test.o):
+ (test.p):
+ (test.q):
+ (test.r):
+ (test.s):
+ (test.t):
+ (test.u):
+ (test.v):
+ (test.w):
+ (test.x):
+ (test.y):
+ (test.z):
+ (foo):
+ (bar):
+ (falsey.bar):
+ (baz):
+ (falsey.baz):
+
</ins><span class="cx"> 2016-04-03 Yusuke Suzuki <utatane.tea@gmail.com>
</span><span class="cx">
</span><span class="cx"> Unreviewed, turn ES6 for-in loop test success
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -861,6 +861,8 @@
</span><span class="cx"> pushScopedControlFlowContext();
</span><span class="cx"> m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(*m_vm, functionSymbolTable), m_lexicalEnvironmentRegister, false, symbolTableConstantIndex });
</span><span class="cx">
</span><ins>+ m_varScopeSymbolTableIndex = m_symbolTableStack.size() - 1;
+
</ins><span class="cx"> // This completes step 28 of section 9.2.12.
</span><span class="cx"> for (unsigned i = 0; i < valuesToMoveIntoVars.size(); i++) {
</span><span class="cx"> ASSERT(!isSimpleParameterList);
</span><span class="lines">@@ -1931,6 +1933,18 @@
</span><span class="cx"> emitNewFunctionExpressionCommon(temp.get(), function);
</span><span class="cx"> bool isLexicallyScoped = true;
</span><span class="cx"> emitPutToScope(scope, variableForLocalEntry(name, entry, symbolTableIndex, isLexicallyScoped), temp.get(), DoNotThrowIfNotFound, Initialization);
</span><ins>+
+ if (iter->value.isSloppyModeHoistingCandidate() && m_scopeNode->hasSloppyModeHoistedFunction(name.impl())) {
+ ASSERT(m_varScopeSymbolTableIndex);
+ ASSERT(*m_varScopeSymbolTableIndex < m_symbolTableStack.size());
+ SymbolTableStackEntry& varScope = m_symbolTableStack[*m_varScopeSymbolTableIndex];
+ SymbolTable* varSymbolTable = varScope.m_symbolTable.get();
+ RELEASE_ASSERT(varSymbolTable->scopeType() == SymbolTable::ScopeType::VarScope);
+ SymbolTableEntry entry = varSymbolTable->get(name.impl());
+ RELEASE_ASSERT(!entry.isNull());
+ bool isLexicallyScoped = false;
+ emitPutToScope(varScope.m_scope, variableForLocalEntry(name, entry, varScope.m_symbolTableConstantIndex, isLexicallyScoped), temp.get(), DoNotThrowIfNotFound, Initialization);
+ }
</ins><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -863,6 +863,7 @@
</span><span class="cx"> };
</span><span class="cx"> Vector<SymbolTableStackEntry> m_symbolTableStack;
</span><span class="cx"> Vector<std::pair<VariableEnvironment, TDZCheckOptimization>> m_TDZStack;
</span><ins>+ Optional<size_t> m_varScopeSymbolTableIndex;
</ins><span class="cx"> void pushTDZVariables(VariableEnvironment, TDZCheckOptimization);
</span><span class="cx">
</span><span class="cx"> ScopeNode* const m_scopeNode;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreparserNodescpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/parser/Nodes.cpp (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/parser/Nodes.cpp        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/parser/Nodes.cpp        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -93,7 +93,7 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx">
</span><del>-ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, const SourceCode& source, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
</del><ins>+ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, const SourceCode& source, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
</ins><span class="cx"> : StatementNode(endLocation)
</span><span class="cx"> , ParserArenaRoot(parserArena)
</span><span class="cx"> , VariableEnvironmentNode(lexicalVariables, WTFMove(funcStack))
</span><span class="lines">@@ -103,6 +103,7 @@
</span><span class="cx"> , m_features(features)
</span><span class="cx"> , m_innerArrowFunctionCodeFeatures(innerArrowFunctionCodeFeatures)
</span><span class="cx"> , m_source(source)
</span><ins>+ , m_sloppyModeHoistedFunctions(WTFMove(sloppyModeHoistedFunctions))
</ins><span class="cx"> , m_numConstants(numConstants)
</span><span class="cx"> , m_statements(children)
</span><span class="cx"> {
</span><span class="lines">@@ -116,8 +117,8 @@
</span><span class="cx">
</span><span class="cx"> // ------------------------------ ProgramNode -----------------------------
</span><span class="cx">
</span><del>-ProgramNode::ProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
- : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
</del><ins>+ProgramNode::ProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
+ : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
</ins><span class="cx"> , m_startColumn(startColumn)
</span><span class="cx"> , m_endColumn(endColumn)
</span><span class="cx"> {
</span><span class="lines">@@ -125,8 +126,8 @@
</span><span class="cx">
</span><span class="cx"> // ------------------------------ ModuleProgramNode -----------------------------
</span><span class="cx">
</span><del>-ModuleProgramNode::ModuleProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
- : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
</del><ins>+ModuleProgramNode::ModuleProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
+ : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
</ins><span class="cx"> , m_startColumn(startColumn)
</span><span class="cx"> , m_endColumn(endColumn)
</span><span class="cx"> {
</span><span class="lines">@@ -134,8 +135,8 @@
</span><span class="cx">
</span><span class="cx"> // ------------------------------ EvalNode -----------------------------
</span><span class="cx">
</span><del>-EvalNode::EvalNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
- : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
</del><ins>+EvalNode::EvalNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
+ : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
</ins><span class="cx"> , m_endColumn(endColumn)
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="lines">@@ -180,8 +181,8 @@
</span><span class="cx">
</span><span class="cx"> // ------------------------------ FunctionNode -----------------------------
</span><span class="cx">
</span><del>-FunctionNode::FunctionNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, FunctionParameters* parameters, const SourceCode& sourceCode, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
- : ScopeNode(parserArena, startLocation, endLocation, sourceCode, children, varEnvironment, WTFMove(funcStack), lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
</del><ins>+FunctionNode::FunctionNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters* parameters, const SourceCode& sourceCode, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
+ : ScopeNode(parserArena, startLocation, endLocation, sourceCode, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
</ins><span class="cx"> , m_parameters(parameters)
</span><span class="cx"> , m_startColumn(startColumn)
</span><span class="cx"> , m_endColumn(endColumn)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreparserNodesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/parser/Nodes.h (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/parser/Nodes.h        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/parser/Nodes.h        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -36,6 +36,7 @@
</span><span class="cx"> #include "SymbolTable.h"
</span><span class="cx"> #include "VariableEnvironment.h"
</span><span class="cx"> #include <wtf/MathExtras.h>
</span><ins>+#include <wtf/SmallPtrSet.h>
</ins><span class="cx">
</span><span class="cx"> namespace JSC {
</span><span class="cx">
</span><span class="lines">@@ -51,6 +52,8 @@
</span><span class="cx"> class ScopeNode;
</span><span class="cx"> class ModuleAnalyzer;
</span><span class="cx">
</span><ins>+ typedef SmallPtrSet<UniquedStringImpl*> UniquedStringImplPtrSet;
+
</ins><span class="cx"> enum Operator {
</span><span class="cx"> OpEqual,
</span><span class="cx"> OpPlusEq,
</span><span class="lines">@@ -1563,7 +1566,7 @@
</span><span class="cx"> public:
</span><span class="cx">
</span><span class="cx"> ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, bool inStrictContext);
</span><del>- ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, const SourceCode&, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
</del><ins>+ ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, const SourceCode&, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
</ins><span class="cx">
</span><span class="cx"> using ParserArenaRoot::operator new;
</span><span class="cx">
</span><span class="lines">@@ -1601,6 +1604,7 @@
</span><span class="cx"> bool hasCapturedVariables() const { return m_varDeclarations.hasCapturedVariables(); }
</span><span class="cx"> bool captures(UniquedStringImpl* uid) { return m_varDeclarations.captures(uid); }
</span><span class="cx"> bool captures(const Identifier& ident) { return captures(ident.impl()); }
</span><ins>+ bool hasSloppyModeHoistedFunction(UniquedStringImpl* uid) const { return m_sloppyModeHoistedFunctions.contains(uid); }
</ins><span class="cx">
</span><span class="cx"> VariableEnvironment& varDeclarations() { return m_varDeclarations; }
</span><span class="cx">
</span><span class="lines">@@ -1627,13 +1631,14 @@
</span><span class="cx"> InnerArrowFunctionCodeFeatures m_innerArrowFunctionCodeFeatures;
</span><span class="cx"> SourceCode m_source;
</span><span class="cx"> VariableEnvironment m_varDeclarations;
</span><ins>+ UniquedStringImplPtrSet m_sloppyModeHoistedFunctions;
</ins><span class="cx"> int m_numConstants;
</span><span class="cx"> SourceElements* m_statements;
</span><span class="cx"> };
</span><span class="cx">
</span><span class="cx"> class ProgramNode : public ScopeNode {
</span><span class="cx"> public:
</span><del>- ProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
</del><ins>+ ProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
</ins><span class="cx">
</span><span class="cx"> unsigned startColumn() const { return m_startColumn; }
</span><span class="cx"> unsigned endColumn() const { return m_endColumn; }
</span><span class="lines">@@ -1648,7 +1653,7 @@
</span><span class="cx">
</span><span class="cx"> class EvalNode : public ScopeNode {
</span><span class="cx"> public:
</span><del>- EvalNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
</del><ins>+ EvalNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
</ins><span class="cx">
</span><span class="cx"> ALWAYS_INLINE unsigned startColumn() const { return 0; }
</span><span class="cx"> unsigned endColumn() const { return m_endColumn; }
</span><span class="lines">@@ -1663,7 +1668,7 @@
</span><span class="cx">
</span><span class="cx"> class ModuleProgramNode : public ScopeNode {
</span><span class="cx"> public:
</span><del>- ModuleProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
</del><ins>+ ModuleProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
</ins><span class="cx">
</span><span class="cx"> unsigned startColumn() const { return m_startColumn; }
</span><span class="cx"> unsigned endColumn() const { return m_endColumn; }
</span><span class="lines">@@ -1904,7 +1909,7 @@
</span><span class="cx">
</span><span class="cx"> class FunctionNode final : public ScopeNode {
</span><span class="cx"> public:
</span><del>- FunctionNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
</del><ins>+ FunctionNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
</ins><span class="cx">
</span><span class="cx"> FunctionParameters* parameters() const { return m_parameters; }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreparserParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/parser/Parser.cpp (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/parser/Parser.cpp        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/parser/Parser.cpp        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -203,6 +203,7 @@
</span><span class="cx"> , m_superBinding(superBinding)
</span><span class="cx"> , m_defaultConstructorKind(defaultConstructorKind)
</span><span class="cx"> , m_thisTDZMode(thisTDZMode)
</span><ins>+ , m_immediateParentAllowsFunctionDeclarationInStatement(false)
</ins><span class="cx"> {
</span><span class="cx"> m_lexer = std::make_unique<LexerType>(vm, builtinMode);
</span><span class="cx"> m_lexer->setCode(source, &m_parserArena);
</span><span class="lines">@@ -304,9 +305,11 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> IdentifierSet capturedVariables;
</span><ins>+ UniquedStringImplPtrSet sloppyModeHoistedFunctions;
</ins><span class="cx"> bool modifiedParameter = false;
</span><span class="cx"> bool modifiedArguments = false;
</span><del>- scope->getCapturedVars(capturedVariables, modifiedParameter, modifiedArguments);
</del><ins>+ scope->getSloppyModeHoistedFunctions(sloppyModeHoistedFunctions);
+ scope->getCapturedVars(capturedVariables, modifiedParameter, modifiedArguments);
</ins><span class="cx">
</span><span class="cx"> VariableEnvironment& varDeclarations = scope->declaredVariables();
</span><span class="cx"> for (auto& entry : capturedVariables)
</span><span class="lines">@@ -341,19 +344,20 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx"> #endif // NDEBUG
</span><del>- didFinishParsing(sourceElements, scope->takeFunctionDeclarations(), varDeclarations, features, context.numConstants());
</del><ins>+ didFinishParsing(sourceElements, scope->takeFunctionDeclarations(), varDeclarations, WTFMove(sloppyModeHoistedFunctions), features, context.numConstants());
</ins><span class="cx">
</span><span class="cx"> return parseError;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> template <typename LexerType>
</span><span class="cx"> void Parser<LexerType>::didFinishParsing(SourceElements* sourceElements, DeclarationStacks::FunctionStack&& funcStack,
</span><del>- VariableEnvironment& varDeclarations, CodeFeatures features, int numConstants)
</del><ins>+ VariableEnvironment& varDeclarations, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, CodeFeatures features, int numConstants)
</ins><span class="cx"> {
</span><span class="cx"> m_sourceElements = sourceElements;
</span><span class="cx"> m_funcDeclarations = WTFMove(funcStack);
</span><span class="cx"> m_varDeclarations.swap(varDeclarations);
</span><span class="cx"> m_features = features;
</span><ins>+ m_sloppyModeHoistedFunctions = WTFMove(sloppyModeHoistedFunctions);
</ins><span class="cx"> m_numConstants = numConstants;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -562,8 +566,10 @@
</span><span class="cx"> }
</span><span class="cx"> if (shouldParseVariableDeclaration)
</span><span class="cx"> result = parseVariableDeclaration(context, DeclarationType::LetDeclaration);
</span><del>- else
- result = parseExpressionOrLabelStatement(context); // Treat this as an IDENT. This is how ::parseStatement() handles IDENT.
</del><ins>+ else {
+ bool allowFunctionDeclarationAsStatement = true;
+ result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement);
+ }
</ins><span class="cx">
</span><span class="cx"> break;
</span><span class="cx"> }
</span><span class="lines">@@ -575,6 +581,16 @@
</span><span class="cx"> case FUNCTION:
</span><span class="cx"> result = parseFunctionDeclaration(context);
</span><span class="cx"> break;
</span><ins>+ case IDENT:
+ case YIELD: {
+ // This is a convenient place to notice labeled statements
+ // (even though we also parse them as normal statements)
+ // because we allow the following type of code in sloppy mode:
+ // ``` function foo() { label: function bar() { } } ```
+ bool allowFunctionDeclarationAsStatement = true;
+ result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement);
+ break;
+ }
</ins><span class="cx"> default:
</span><span class="cx"> m_statementDepth--; // parseStatement() increments the depth.
</span><span class="cx"> result = parseStatement(context, directive, directiveLiteralLength);
</span><span class="lines">@@ -1603,6 +1619,8 @@
</span><span class="cx"> failIfStackOverflow();
</span><span class="cx"> TreeStatement result = 0;
</span><span class="cx"> bool shouldSetEndOffset = true;
</span><ins>+ bool parentAllowsFunctionDeclarationAsStatement = m_immediateParentAllowsFunctionDeclarationInStatement;
+ m_immediateParentAllowsFunctionDeclarationInStatement = false;
</ins><span class="cx">
</span><span class="cx"> switch (m_token.m_type) {
</span><span class="cx"> case OPENBRACE:
</span><span class="lines">@@ -1612,12 +1630,46 @@
</span><span class="cx"> case VAR:
</span><span class="cx"> result = parseVariableDeclaration(context, DeclarationType::VarDeclaration);
</span><span class="cx"> break;
</span><del>- case FUNCTION:
- if (!strictMode())
- result = parseFunctionDeclaration(context);
- else
</del><ins>+ case FUNCTION: {
+ if (!strictMode()) {
+ failIfFalse(parentAllowsFunctionDeclarationAsStatement, "Function declarations are only allowed inside block statements or at the top level of a program");
+ if (currentScope()->isFunction()) {
+ // Any function declaration that isn't in a block is a syntax error unless it's
+ // in an if/else statement. If it's in an if/else statement, we will magically
+ // treat it as if the if/else statement is inside a block statement.
+ // to the very top like a "var". For example:
+ // function a() {
+ // if (cond) function foo() { }
+ // }
+ // will be rewritten as:
+ // function a() {
+ // if (cond) { function foo() { } }
+ // }
+ AutoPopScopeRef blockScope(this, pushScope());
+ blockScope->setIsLexicalScope();
+ blockScope->preventVarDeclarations();
+ JSTokenLocation location(tokenLocation());
+ int start = tokenLine();
+
+ TreeStatement function = parseFunctionDeclaration(context);
+ propagateError();
+ failIfFalse(function, "Expected valid function statement after 'function' keyword");
+ TreeSourceElements sourceElements = context.createSourceElements();
+ context.appendStatement(sourceElements, function);
+ result = context.createBlockStatement(location, sourceElements, start, m_lastTokenEndPosition.line, currentScope()->finalizeLexicalEnvironment(), currentScope()->takeFunctionDeclarations());
+ popScope(blockScope, TreeBuilder::NeedsFreeVariableInfo);
+ } else {
+ // We only implement annex B.3.3 if we're in function mode. Otherwise, we fall back
+ // to hoisting behavior.
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=155813
+ DepthManager statementDepth(&m_statementDepth);
+ m_statementDepth = 1;
+ result = parseFunctionDeclaration(context);
+ }
+ } else
</ins><span class="cx"> failWithMessage("Function declarations are only allowed inside blocks or switch statements in strict mode");
</span><span class="cx"> break;
</span><ins>+ }
</ins><span class="cx"> case SEMICOLON: {
</span><span class="cx"> JSTokenLocation location(tokenLocation());
</span><span class="cx"> next();
</span><span class="lines">@@ -1667,9 +1719,11 @@
</span><span class="cx"> // These tokens imply the end of a set of source elements
</span><span class="cx"> return 0;
</span><span class="cx"> case IDENT:
</span><del>- case YIELD:
- result = parseExpressionOrLabelStatement(context);
</del><ins>+ case YIELD: {
+ bool allowFunctionDeclarationAsStatement = false;
+ result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement);
</ins><span class="cx"> break;
</span><ins>+ }
</ins><span class="cx"> case STRING:
</span><span class="cx"> directive = m_token.m_data.ident;
</span><span class="cx"> if (directiveLiteralLength)
</span><span class="lines">@@ -2372,7 +2426,7 @@
</span><span class="cx"> };
</span><span class="cx">
</span><span class="cx"> template <typename LexerType>
</span><del>-template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrLabelStatement(TreeBuilder& context)
</del><ins>+template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrLabelStatement(TreeBuilder& context, bool allowFunctionDeclarationAsStatement)
</ins><span class="cx"> {
</span><span class="cx">
</span><span class="cx"> /* Expression and Label statements are ambiguous at LL(1), so we have a
</span><span class="lines">@@ -2423,6 +2477,7 @@
</span><span class="cx"> for (size_t i = 0; i < labels.size(); i++)
</span><span class="cx"> pushLabel(labels[i].m_ident, isLoop);
</span><span class="cx"> }
</span><ins>+ m_immediateParentAllowsFunctionDeclarationInStatement = allowFunctionDeclarationAsStatement;
</ins><span class="cx"> TreeStatement statement = parseStatement(context, unused);
</span><span class="cx"> if (!m_syntaxAlreadyValidated) {
</span><span class="cx"> for (size_t i = 0; i < labels.size(); i++)
</span><span class="lines">@@ -2475,6 +2530,7 @@
</span><span class="cx"> handleProductionOrFail(CLOSEPAREN, ")", "end", "'if' condition");
</span><span class="cx">
</span><span class="cx"> const Identifier* unused = 0;
</span><ins>+ m_immediateParentAllowsFunctionDeclarationInStatement = true;
</ins><span class="cx"> TreeStatement trueBlock = parseStatement(context, unused);
</span><span class="cx"> failIfFalse(trueBlock, "Expected a statement as the body of an if block");
</span><span class="cx">
</span><span class="lines">@@ -2491,6 +2547,7 @@
</span><span class="cx"> next();
</span><span class="cx"> if (!match(IF)) {
</span><span class="cx"> const Identifier* unused = 0;
</span><ins>+ m_immediateParentAllowsFunctionDeclarationInStatement = true;
</ins><span class="cx"> TreeStatement block = parseStatement(context, unused);
</span><span class="cx"> failIfFalse(block, "Expected a statement as the body of an else block");
</span><span class="cx"> statementStack.append(block);
</span><span class="lines">@@ -2507,6 +2564,7 @@
</span><span class="cx"> int innerEnd = tokenLine();
</span><span class="cx"> handleProductionOrFail(CLOSEPAREN, ")", "end", "'if' condition");
</span><span class="cx"> const Identifier* unused = 0;
</span><ins>+ m_immediateParentAllowsFunctionDeclarationInStatement = true;
</ins><span class="cx"> TreeStatement innerTrueBlock = parseStatement(context, unused);
</span><span class="cx"> failIfFalse(innerTrueBlock, "Expected a statement as the body of an if block");
</span><span class="cx"> tokenLocationStack.append(tempLocation);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreparserParserh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/parser/Parser.h (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/parser/Parser.h        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/parser/Parser.h        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -40,7 +40,6 @@
</span><span class="cx"> #include <wtf/Forward.h>
</span><span class="cx"> #include <wtf/Noncopyable.h>
</span><span class="cx"> #include <wtf/RefPtr.h>
</span><del>-#include <wtf/SmallPtrSet.h>
</del><span class="cx">
</span><span class="cx"> namespace JSC {
</span><span class="cx">
</span><span class="lines">@@ -52,8 +51,6 @@
</span><span class="cx"> class ProgramNode;
</span><span class="cx"> class SourceCode;
</span><span class="cx">
</span><del>-typedef SmallPtrSet<UniquedStringImpl*> UniquedStringImplPtrSet;
-
</del><span class="cx"> // Macros to make the more common TreeBuilder types a little less verbose
</span><span class="cx"> #define TreeStatement typename TreeBuilder::Statement
</span><span class="cx"> #define TreeExpression typename TreeBuilder::Expression
</span><span class="lines">@@ -369,7 +366,7 @@
</span><span class="cx"> return result;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- DeclarationResultMask declareFunction(const Identifier* ident, bool declareAsVar)
</del><ins>+ DeclarationResultMask declareFunction(const Identifier* ident, bool declareAsVar, bool isSloppyModeHoistingCandidate)
</ins><span class="cx"> {
</span><span class="cx"> ASSERT(m_allowsVarDeclarations || m_allowsLexicalDeclarations);
</span><span class="cx"> DeclarationResultMask result = DeclarationResult::Valid;
</span><span class="lines">@@ -378,7 +375,8 @@
</span><span class="cx"> result |= DeclarationResult::InvalidStrictMode;
</span><span class="cx"> m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
</span><span class="cx"> auto addResult = declareAsVar ? m_declaredVariables.add(ident->impl()) : m_lexicalVariables.add(ident->impl());
</span><del>- addResult.iterator->value.setIsFunction();
</del><ins>+ if (isSloppyModeHoistingCandidate)
+ addResult.iterator->value.setIsSloppyModeHoistingCandidate();
</ins><span class="cx"> if (declareAsVar) {
</span><span class="cx"> addResult.iterator->value.setIsVar();
</span><span class="cx"> if (m_lexicalVariables.contains(ident->impl()))
</span><span class="lines">@@ -386,12 +384,23 @@
</span><span class="cx"> } else {
</span><span class="cx"> addResult.iterator->value.setIsLet();
</span><span class="cx"> ASSERT_WITH_MESSAGE(!m_declaredVariables.size(), "We should only declare a function as a lexically scoped variable in scopes where var declarations aren't allowed. I.e, in strict mode and not at the top-level scope of a function or program.");
</span><del>- if (!addResult.isNewEntry)
- result |= DeclarationResult::InvalidDuplicateDeclaration;
</del><ins>+ if (!addResult.isNewEntry) {
+ if (!isSloppyModeHoistingCandidate || !addResult.iterator->value.isFunction())
+ result |= DeclarationResult::InvalidDuplicateDeclaration;
+ }
</ins><span class="cx"> }
</span><ins>+
+ addResult.iterator->value.setIsFunction();
+
</ins><span class="cx"> return result;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ void addSloppyModeHoistableFunctionCandidate(const Identifier* ident)
+ {
+ ASSERT(m_allowsVarDeclarations);
+ m_sloppyModeHoistableFunctionCandidates.add(ident->impl());
+ }
+
</ins><span class="cx"> void appendFunction(FunctionMetadataNode* node)
</span><span class="cx"> {
</span><span class="cx"> ASSERT(node);
</span><span class="lines">@@ -478,6 +487,7 @@
</span><span class="cx"> bool isArgumentsIdent = isArguments(m_vm, ident);
</span><span class="cx"> auto addResult = m_declaredVariables.add(ident->impl());
</span><span class="cx"> addResult.iterator->value.clearIsVar();
</span><ins>+ addResult.iterator->value.setIsParameter();
</ins><span class="cx"> bool isValidStrictMode = addResult.isNewEntry && m_vm->propertyNames->eval != *ident && !isArgumentsIdent;
</span><span class="cx"> m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
</span><span class="cx"> m_declaredParameters.add(ident->impl());
</span><span class="lines">@@ -603,6 +613,25 @@
</span><span class="cx"> m_innerArrowFunctionFeatures = m_innerArrowFunctionFeatures | arrowFunctionCodeFeatures;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ void getSloppyModeHoistedFunctions(UniquedStringImplPtrSet& sloppyModeHoistedFunctions)
+ {
+ for (UniquedStringImpl* function : m_sloppyModeHoistableFunctionCandidates) {
+ // ES6 Annex B.3.3. The only time we can't hoist a function is if a syntax error would
+ // be caused by declaring a var with that function's name or if we have a parameter with
+ // that function's name. Note that we would only cause a syntax error if we had a let/const/class
+ // variable with the same name.
+ if (!m_lexicalVariables.contains(function)) {
+ auto iter = m_declaredVariables.find(function);
+ bool isParameter = iter != m_declaredVariables.end() && iter->value.isParameter();
+ if (!isParameter) {
+ auto addResult = m_declaredVariables.add(function);
+ addResult.iterator->value.setIsVar();
+ sloppyModeHoistedFunctions.add(function);
+ }
+ }
+ }
+ }
+
</ins><span class="cx"> void getCapturedVars(IdentifierSet& capturedVariables, bool& modifiedParameter, bool& modifiedArguments)
</span><span class="cx"> {
</span><span class="cx"> if (m_needsFullActivation || m_usesEval) {
</span><span class="lines">@@ -742,6 +771,7 @@
</span><span class="cx"> VariableEnvironment m_declaredVariables;
</span><span class="cx"> VariableEnvironment m_lexicalVariables;
</span><span class="cx"> Vector<UniquedStringImplPtrSet, 6> m_usedVariables;
</span><ins>+ UniquedStringImplPtrSet m_sloppyModeHoistableFunctionCandidates;
</ins><span class="cx"> IdentifierSet m_closedVariableCandidates;
</span><span class="cx"> UniquedStringImplPtrSet m_writtenVariables;
</span><span class="cx"> RefPtr<ModuleScopeData> m_moduleScopeData;
</span><span class="lines">@@ -770,6 +800,17 @@
</span><span class="cx"> return ScopeRef(m_scopeStack, m_index - 1);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ bool operator==(const ScopeRef& other)
+ {
+ ASSERT(other.m_scopeStack == m_scopeStack);
+ return m_index == other.m_index;
+ }
+
+ bool operator!=(const ScopeRef& other)
+ {
+ return !(*this == other);
+ }
+
</ins><span class="cx"> private:
</span><span class="cx"> ScopeStack* m_scopeStack;
</span><span class="cx"> unsigned m_index;
</span><span class="lines">@@ -1076,18 +1117,40 @@
</span><span class="cx">
</span><span class="cx"> std::pair<DeclarationResultMask, ScopeRef> declareFunction(const Identifier* ident)
</span><span class="cx"> {
</span><del>- if (m_statementDepth == 1 || !strictMode()) {
</del><ins>+ if (m_statementDepth == 1 || (!strictMode() && !currentScope()->isFunction())) {
</ins><span class="cx"> // Functions declared at the top-most scope (both in sloppy and strict mode) are declared as vars
</span><span class="cx"> // for backwards compatibility. This allows us to declare functions with the same name more than once.
</span><span class="cx"> // In sloppy mode, we always declare functions as vars.
</span><span class="cx"> bool declareAsVar = true;
</span><ins>+ bool isSloppyModeHoistingCandidate = false;
</ins><span class="cx"> ScopeRef variableScope = currentVariableScope();
</span><del>- return std::make_pair(variableScope->declareFunction(ident, declareAsVar), variableScope);
</del><ins>+ return std::make_pair(variableScope->declareFunction(ident, declareAsVar, isSloppyModeHoistingCandidate), variableScope);
</ins><span class="cx"> }
</span><span class="cx">
</span><ins>+ if (!strictMode()) {
+ ASSERT(currentScope()->isFunction());
+
+ // Functions declared inside a function inside a nested block scope in sloppy mode are subject to this
+ // crazy rule defined inside Annex B.3.3 in the ES6 spec. It basically states that we will create
+ // the function as a local block scoped variable, but when we evaluate the block that the function is
+ // contained in, we will assign the function to a "var" variable only if declaring such a "var" wouldn't
+ // be a syntax error and if there isn't a parameter with the same name. (It would only be a syntax error if
+ // there are is a let/class/const with the same name). Note that this mean we only do the "var" hoisting
+ // binding if the block evaluates. For example, this means we wont won't perform the binding if it's inside
+ // the untaken branch of an if statement.
+ bool declareAsVar = false;
+ bool isSloppyModeHoistingCandidate = true;
+ ScopeRef lexicalVariableScope = currentLexicalDeclarationScope();
+ ScopeRef varScope = currentVariableScope();
+ varScope->addSloppyModeHoistableFunctionCandidate(ident);
+ ASSERT(varScope != lexicalVariableScope);
+ return std::make_pair(lexicalVariableScope->declareFunction(ident, declareAsVar, isSloppyModeHoistingCandidate), lexicalVariableScope);
+ }
+
</ins><span class="cx"> bool declareAsVar = false;
</span><ins>+ bool isSloppyModeHoistingCandidate = false;
</ins><span class="cx"> ScopeRef lexicalVariableScope = currentLexicalDeclarationScope();
</span><del>- return std::make_pair(lexicalVariableScope->declareFunction(ident, declareAsVar), lexicalVariableScope);
</del><ins>+ return std::make_pair(lexicalVariableScope->declareFunction(ident, declareAsVar, isSloppyModeHoistingCandidate), lexicalVariableScope);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> NEVER_INLINE bool hasDeclaredVariable(const Identifier& ident)
</span><span class="lines">@@ -1134,7 +1197,7 @@
</span><span class="cx"> Parser();
</span><span class="cx"> String parseInner(const Identifier&, SourceParseMode);
</span><span class="cx">
</span><del>- void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&&, VariableEnvironment&, CodeFeatures, int);
</del><ins>+ void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, CodeFeatures, int);
</ins><span class="cx">
</span><span class="cx"> // Used to determine type of error to report.
</span><span class="cx"> bool isFunctionMetadataNode(ScopeNode*) { return false; }
</span><span class="lines">@@ -1349,7 +1412,7 @@
</span><span class="cx"> template <class TreeBuilder> TreeStatement parseTryStatement(TreeBuilder&);
</span><span class="cx"> template <class TreeBuilder> TreeStatement parseDebuggerStatement(TreeBuilder&);
</span><span class="cx"> template <class TreeBuilder> TreeStatement parseExpressionStatement(TreeBuilder&);
</span><del>- template <class TreeBuilder> TreeStatement parseExpressionOrLabelStatement(TreeBuilder&);
</del><ins>+ template <class TreeBuilder> TreeStatement parseExpressionOrLabelStatement(TreeBuilder&, bool allowFunctionDeclarationAsStatement);
</ins><span class="cx"> template <class TreeBuilder> TreeStatement parseIfStatement(TreeBuilder&);
</span><span class="cx"> template <class TreeBuilder> TreeStatement parseBlockStatement(TreeBuilder&);
</span><span class="cx"> template <class TreeBuilder> TreeExpression parseExpression(TreeBuilder&);
</span><span class="lines">@@ -1544,10 +1607,12 @@
</span><span class="cx"> ThisTDZMode m_thisTDZMode;
</span><span class="cx"> VariableEnvironment m_varDeclarations;
</span><span class="cx"> DeclarationStacks::FunctionStack m_funcDeclarations;
</span><ins>+ UniquedStringImplPtrSet m_sloppyModeHoistedFunctions;
</ins><span class="cx"> CodeFeatures m_features;
</span><span class="cx"> int m_numConstants;
</span><span class="cx"> ExpressionErrorClassifier* m_expressionErrorClassifier;
</span><span class="cx"> bool m_isEvalContext;
</span><ins>+ bool m_immediateParentAllowsFunctionDeclarationInStatement;
</ins><span class="cx">
</span><span class="cx"> struct DepthManager {
</span><span class="cx"> DepthManager(int* depth)
</span><span class="lines">@@ -1617,6 +1682,7 @@
</span><span class="cx"> m_varDeclarations,
</span><span class="cx"> WTFMove(m_funcDeclarations),
</span><span class="cx"> currentScope()->finalizeLexicalEnvironment(),
</span><ins>+ WTFMove(m_sloppyModeHoistedFunctions),
</ins><span class="cx"> m_parameters,
</span><span class="cx"> *m_source,
</span><span class="cx"> m_features,
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreparserVariableEnvironmenth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/parser/VariableEnvironment.h (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/parser/VariableEnvironment.h        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/parser/VariableEnvironment.h        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -41,6 +41,8 @@
</span><span class="cx"> ALWAYS_INLINE bool isImported() const { return m_bits & IsImported; }
</span><span class="cx"> ALWAYS_INLINE bool isImportedNamespace() const { return m_bits & IsImportedNamespace; }
</span><span class="cx"> ALWAYS_INLINE bool isFunction() const { return m_bits & IsFunction; }
</span><ins>+ ALWAYS_INLINE bool isParameter() const { return m_bits & IsParameter; }
+ ALWAYS_INLINE bool isSloppyModeHoistingCandidate() const { return m_bits & IsSloppyModeHoistingCandidate; }
</ins><span class="cx">
</span><span class="cx"> ALWAYS_INLINE void setIsCaptured() { m_bits |= IsCaptured; }
</span><span class="cx"> ALWAYS_INLINE void setIsConst() { m_bits |= IsConst; }
</span><span class="lines">@@ -50,11 +52,13 @@
</span><span class="cx"> ALWAYS_INLINE void setIsImported() { m_bits |= IsImported; }
</span><span class="cx"> ALWAYS_INLINE void setIsImportedNamespace() { m_bits |= IsImportedNamespace; }
</span><span class="cx"> ALWAYS_INLINE void setIsFunction() { m_bits |= IsFunction; }
</span><ins>+ ALWAYS_INLINE void setIsParameter() { m_bits |= IsParameter; }
+ ALWAYS_INLINE void setIsSloppyModeHoistingCandidate() { m_bits |= IsSloppyModeHoistingCandidate; }
</ins><span class="cx">
</span><span class="cx"> ALWAYS_INLINE void clearIsVar() { m_bits &= ~IsVar; }
</span><span class="cx">
</span><span class="cx"> private:
</span><del>- enum Traits : uint8_t {
</del><ins>+ enum Traits : uint16_t {
</ins><span class="cx"> IsCaptured = 1 << 0,
</span><span class="cx"> IsConst = 1 << 1,
</span><span class="cx"> IsVar = 1 << 2,
</span><span class="lines">@@ -62,9 +66,11 @@
</span><span class="cx"> IsExported = 1 << 4,
</span><span class="cx"> IsImported = 1 << 5,
</span><span class="cx"> IsImportedNamespace = 1 << 6,
</span><del>- IsFunction = 1 << 7
</del><ins>+ IsFunction = 1 << 7,
+ IsParameter = 1 << 8,
+ IsSloppyModeHoistingCandidate = 1 << 9
</ins><span class="cx"> };
</span><del>- uint8_t m_bits { 0 };
</del><ins>+ uint16_t m_bits { 0 };
</ins><span class="cx"> };
</span><span class="cx">
</span><span class="cx"> struct VariableEnvironmentEntryHashTraits : HashTraits<VariableEnvironmentEntry> {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeCodeCacheh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/CodeCache.h (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/CodeCache.h        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/runtime/CodeCache.h        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -32,7 +32,6 @@
</span><span class="cx"> #include "SourceCode.h"
</span><span class="cx"> #include "SourceCodeKey.h"
</span><span class="cx"> #include "Strong.h"
</span><del>-#include "VariableEnvironment.h"
</del><span class="cx"> #include <wtf/CurrentTime.h>
</span><span class="cx"> #include <wtf/Forward.h>
</span><span class="cx"> #include <wtf/RandomNumber.h>
</span><span class="lines">@@ -57,6 +56,7 @@
</span><span class="cx"> class VM;
</span><span class="cx"> class SourceCode;
</span><span class="cx"> class SourceProvider;
</span><ins>+class VariableEnvironment;
</ins><span class="cx">
</span><span class="cx"> struct SourceCodeValue {
</span><span class="cx"> SourceCodeValue()
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSScopecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSScope.cpp (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSScope.cpp        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/runtime/JSScope.cpp        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -32,6 +32,7 @@
</span><span class="cx"> #include "JSModuleRecord.h"
</span><span class="cx"> #include "JSWithScope.h"
</span><span class="cx"> #include "JSCInlines.h"
</span><ins>+#include "VariableEnvironment.h"
</ins><span class="cx">
</span><span class="cx"> namespace JSC {
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSScopeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSScope.h (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSScope.h        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/runtime/JSScope.h        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -28,12 +28,12 @@
</span><span class="cx">
</span><span class="cx"> #include "GetPutInfo.h"
</span><span class="cx"> #include "JSObject.h"
</span><del>-#include "VariableEnvironment.h"
</del><span class="cx">
</span><span class="cx"> namespace JSC {
</span><span class="cx">
</span><span class="cx"> class ScopeChainIterator;
</span><span class="cx"> class WatchpointSet;
</span><ins>+class VariableEnvironment;
</ins><span class="cx">
</span><span class="cx"> class JSScope : public JSNonFinalObject {
</span><span class="cx"> public:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestses6yaml"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tests/es6.yaml (198988 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/es6.yaml        2016-04-03 19:38:54 UTC (rev 198988)
+++ trunk/Source/JavaScriptCore/tests/es6.yaml        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -881,7 +881,7 @@
</span><span class="cx"> - path: es6/new.target_assignment_is_an_early_error.js
</span><span class="cx"> cmd: runES6 :normal
</span><span class="cx"> - path: es6/non-strict_function_semantics_hoisted_block-level_function_declaration.js
</span><del>- cmd: runES6 :fail
</del><ins>+ cmd: runES6 :normal
</ins><span class="cx"> - path: es6/Promise_is_subclassable_Promise.all.js
</span><span class="cx"> cmd: runES6 :fail
</span><span class="cx"> - path: es6/Promise_is_subclassable_Promise.race.js
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstresssloppymodefunctionhoistingjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/sloppy-mode-function-hoisting.js (0 => 198989)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/sloppy-mode-function-hoisting.js         (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/sloppy-mode-function-hoisting.js        2016-04-03 19:45:05 UTC (rev 198989)
</span><span class="lines">@@ -0,0 +1,655 @@
</span><ins>+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion.");
+}
+
+function test(f, ...args) {
+ for (let i = 0; i < 500; i++)
+ f(...args);
+}
+
+function falsey() { return false; }
+noInline(falsey);
+function truthy() { return true; }
+noInline(truthy);
+
+test(function() {
+ var a;
+ assert(a === undefined);
+ {
+ function a() { return 20; }
+ }
+ assert(a() === 20);
+});
+
+test(function(a) {
+ var a;
+ assert(a === undefined);
+ {
+ function a() { return 20; }
+ }
+ assert(a === undefined);
+});
+
+test(function({a}) {
+ var a;
+ assert(a === undefined);
+ {
+ function a() { return 20; }
+ }
+ assert(a === undefined);
+}, {});
+
+test(function() {
+ let a;
+ assert(a === undefined);
+ {
+ function a() { return 20; }
+ }
+ assert(a === undefined);
+});
+
+test(function() {
+ assert(a === undefined);
+ function foo() { return a(); }
+ {
+ function a() { return 20; }
+ }
+ assert(a() === 20);
+ assert(foo() === 20);
+});
+
+test(function(a = 30) {
+ assert(a === 30);
+ function foo() { return a; }
+ assert(foo() === 30);
+ {
+ function a() { return 20; }
+ assert(a() === 20);
+ assert(foo() === 30);
+ }
+ assert(a === 30);
+ assert(foo() === 30);
+});
+
+test(function() {
+ let x = 15;
+ assert(x === 15);
+ assert(a === undefined);
+ {
+ let x = {x: 20};
+ function a() { return x; }
+ assert(a() === x);
+ assert(a().x === 20);
+ }
+ assert(a().x === 20);
+ assert(x === 15);
+});
+
+test(function() {
+ let x = 15;
+ assert(x === 15);
+ assert(a === undefined);
+ let f;
+ {
+ let x = {x: 20};
+ assert(a() === x);
+ assert(a().x === 20);
+
+ function a() { throw new Error; }
+ function a() { return x; }
+ f = a;
+ }
+ assert(a().x === 20);
+ assert(x === 15);
+ assert(f().x === 20);
+});
+
+test(function() {
+ let x = 15;
+ let f;
+ assert(x === 15);
+ assert(a === undefined);
+ assert(f === undefined);
+ {
+ function a() { return f; }
+ f = a;
+ }
+ assert(x === 15);
+ assert(f() === f);
+});
+
+test(function() {
+ function a() { return 20; }
+ let f = a;
+ assert(a() === 20);
+ {
+ function a() { return 25; }
+ assert(a() === 25);
+ }
+ assert(f() === 20);
+ assert(a() === 25);
+});
+
+test(function() {
+ assert(f === undefined);
+ for (let i = 0; i < 10; i++) {
+ function f() { return i; }
+ assert(f() === i);
+ }
+ assert(f() === 9);
+});
+
+test(function() {
+ assert(f === undefined);
+ let nums = [0, 1, 2, 3];
+ for (let i of nums) {
+ function f() { return i; }
+ assert(f() === i);
+ }
+ assert(f() === 3);
+});
+
+test(function() {
+ assert(f === undefined);
+ let obj = {0:0, 1:1, 2:2, 3:3};
+ for (let i in obj) {
+ function f() { return i; }
+ assert(f() === i);
+ }
+ assert(f() === "3");
+});
+
+test(function() {
+ assert(f === undefined);
+ let nums = [0, 1, 2, 3];
+ let funcs = []
+ for (let i of nums) {
+ function f() { return i; }
+ funcs.push(f);
+ assert(f() === i);
+ }
+ assert(f() === 3);
+ assert(funcs.length === nums.length);
+ for (let i = 0; i < funcs.length; i++) {
+ assert(funcs[i]() === nums[i]);
+ }
+});
+
+test(function() {
+ assert(f === undefined);
+ try {
+ throw new Error("foo");
+ } catch(e) {
+ function f() { return 20; }
+ }
+ assert(f() === 20);
+});
+
+test(function() {
+ assert(f === undefined);
+ try {
+ ;
+ } catch(e) {
+ function f() { return 20; }
+ }
+ assert(f === undefined);
+});
+
+test(function() {
+ assert(foo === undefined);
+ if (falsey()) {
+ function foo() { return 20; }
+ }
+ assert(foo === undefined);
+});
+
+test(function() {
+ assert(foo === undefined);
+ if (falsey())
+ function foo() { return 20; }
+ assert(foo === undefined);
+});
+
+test(function() {
+ assert(foo === undefined);
+ if (truthy()) {
+ function foo() { return 20; }
+ }
+ assert(foo() === 20);
+});
+
+test(function() {
+ assert(foo === undefined);
+ while (truthy()) {
+ break;
+
+ function foo() { return 20; }
+ }
+ assert(foo() === 20);
+});
+
+test(function() {
+ function bar() { return foo; }
+ assert(foo === undefined);
+ assert(bar() === undefined);
+ while (truthy()) {
+ break;
+
+ function foo() { return 20; }
+ }
+ assert(foo() === 20);
+ assert(bar()() === 20);
+});
+
+test(function() {
+ function bar() { return foo; }
+ assert(foo === undefined);
+ assert(bar() === undefined);
+ while (falsey()) {
+ function foo() { return 20; }
+ }
+ assert(foo === undefined);
+ assert(bar() === undefined);
+});
+
+test(function() {
+ var a = "a";
+ assert(a === "a");
+ {
+ let b = 1;
+ assert(a === "a");
+ {
+ let c = 2;
+ assert(a === "a");
+ {
+ let d = 3;
+ function a() { return b + c+ d; }
+ assert(a() === 6);
+ }
+ assert(a() === 6);
+ }
+ assert(a() === 6);
+ }
+ assert(a() === 6);
+});
+
+test(function() {
+ assert(foo === undefined);
+ switch(1) {
+ case 0:
+ function foo() { return 20; }
+ break;
+ }
+ assert(foo() === 20);
+});
+
+test(function() {
+ assert(foo === undefined);
+ switch(1) {
+ case 0:{
+ function foo() { return 20; }
+ break;
+ }
+ }
+ assert(foo === undefined);
+});
+
+test(function() {
+ assert(foo === undefined);
+ switch(0) {
+ case 0:{
+ function foo() { return 20; }
+ break;
+ }
+ }
+ assert(foo() === 20);
+});
+
+test(function() {
+ assert(foo === undefined);
+ switch(0) {
+ case 0:
+ function foo() { return bar; }
+ break;
+ case 1:
+ let bar = 20;
+ break;
+ }
+
+ let threw = false;
+ try {
+ foo();
+ } catch (e) {
+ assert(e instanceof ReferenceError);
+ threw = true;
+ }
+ assert(threw);
+});
+
+test(function() {
+ assert(foo === undefined);
+ switch(0) {
+ case 0:
+ function foo() { return bar; }
+ case 1:
+ let bar = 20;
+ break;
+ }
+
+ assert(foo() === 20);
+});
+
+test(function() {
+ assert(foo === undefined);
+ switch(1) {
+ case 0:
+ function foo() { return bar; }
+ case 1:
+ let bar = 20;
+ assert(foo() === 20);
+ break;
+ }
+
+ assert(foo() === 20);
+});
+
+test(function(a) {
+ assert(a === 25);
+ switch(1) {
+ case 0:
+ function a() { return bar; }
+ case 1:
+ let bar = 20;
+ assert(a() === 20);
+ break;
+ }
+
+ assert(a === 25);
+}, 25);
+
+test(function() {
+ let a = 25;
+ assert(a === 25);
+ switch(1) {
+ case 0:
+ function a() { return bar; }
+ case 1:
+ let bar = 20;
+ assert(a() === 20);
+ break;
+ }
+
+ assert(a === 25);
+});
+
+test(function() {
+ const a = 25;
+ assert(a === 25);
+ switch(1) {
+ case 0:
+ function a() { return bar; }
+ case 1:
+ let bar = 20;
+ assert(a() === 20);
+ break;
+ }
+
+ assert(a === 25);
+});
+
+test(function() {
+ let foo = {};
+ class a { constructor() { return foo; } }
+ assert(new a === foo);
+ switch(1) {
+ case 0:
+ function a() { return bar; }
+ case 1:
+ let bar = 20;
+ assert(a() === 20);
+ break;
+ }
+
+ assert(new a === foo);
+});
+
+test(function() {
+ assert(f === undefined);
+ {
+ if (true)
+ function f() { return 20; }
+ assert(f() === 20);
+ }
+ assert(f() === 20);
+});
+
+test(function() {
+ assert(f === undefined);
+ {
+ if (false)
+ function f() { return 20; }
+ assert(f === undefined);
+ }
+ assert(f === undefined);
+});
+
+test(function() {
+ var x;
+ assert(f === undefined);
+ if (true)
+ if (true)
+ if (true)
+ function f() { return 20; }
+ assert(f() === 20);
+});
+
+test(function() {
+ var x;
+ assert(f === undefined);
+ {
+ if (true)
+ if (false)
+ if (true)
+ function f() { return 20; }
+ }
+ assert(f === undefined);
+});
+
+test(function() {
+ var x;
+ assert(f === undefined);
+ {
+ while (false)
+ while (false)
+ if (true)
+ function f() { return 20; }
+ }
+ assert(f === undefined);
+});
+
+test(function() {
+ assert(f === undefined);
+ var f = 20;
+ assert(f === 20);
+ while (false)
+ while (false)
+ if (true)
+ function f() { return 20; }
+ assert(f === 20);
+});
+
+test(function() {
+ assert(f === undefined);
+ var f = 20;
+ assert(f === 20);
+ var i = 2;
+ {
+ while (i-- > 0)
+ while (i-- > 0)
+ if (true)
+ function f() { return 20; }
+ }
+ assert(f() === 20);
+});
+
+test(function() {
+ assert(f === undefined);
+ var f = 20;
+ assert(f === 20);
+ var i = 2;
+ {
+ while (i-- > 0)
+ while (i-- > 0)
+ if (false)
+ function f() { return 20; }
+ }
+ assert(f === 20);
+});
+
+test(function() {
+ assert(f === undefined);
+ var f = 20;
+ assert(f === 20);
+ var i = 2;
+ {
+ while (i-- > 0)
+ while (i-- > 0)
+ if (false)
+ function f() { return 20; }
+ else
+ function f() { return 30; }
+ }
+ assert(f() === 30);
+});
+
+test(function() {
+ assert(f === undefined);
+ if (true) {
+ label: function f() { return 20; }
+ }
+ assert(f() === 20);
+});
+
+test(function() {
+ assert(f === undefined);
+ if (true) {
+ label: label2: label3: function f() { return 20; }
+ }
+ assert(f() === 20);
+});
+
+test(function() {
+ assert(a === undefined);
+ assert(b === undefined);
+ assert(c === undefined);
+ assert(d === undefined);
+ assert(e === undefined);
+ assert(f === undefined);
+ assert(g === undefined);
+ assert(h === undefined);
+ assert(i === undefined);
+ assert(j === undefined);
+ assert(k === undefined);
+ assert(l === undefined);
+ assert(m === undefined);
+ assert(n === undefined);
+ assert(o === undefined);
+ assert(p === undefined);
+ assert(q === undefined);
+ assert(r === undefined);
+ assert(s === undefined);
+ assert(t === undefined);
+ assert(u === undefined);
+ assert(v === undefined);
+ assert(w === undefined);
+ assert(x === undefined);
+ assert(y === undefined);
+ assert(z === undefined);
+ {
+ function a() { }
+ function b() { }
+ function c() { }
+ function d() { }
+ function e() { }
+ function f() { }
+ function g() { }
+ function h() { }
+ function i() { }
+ function j() { }
+ function k() { }
+ function l() { }
+ function m() { }
+ function n() { }
+ function o() { }
+ function p() { }
+ function q() { }
+ function r() { }
+ function s() { }
+ function t() { }
+ function u() { }
+ function v() { }
+ function w() { }
+ function x() { }
+ function y() { }
+ function z() { }
+ }
+ assert(typeof a === "function");
+ assert(typeof b === "function");
+ assert(typeof c === "function");
+ assert(typeof d === "function");
+ assert(typeof e === "function");
+ assert(typeof f === "function");
+ assert(typeof g === "function");
+ assert(typeof h === "function");
+ assert(typeof i === "function");
+ assert(typeof j === "function");
+ assert(typeof k === "function");
+ assert(typeof l === "function");
+ assert(typeof m === "function");
+ assert(typeof n === "function");
+ assert(typeof o === "function");
+ assert(typeof p === "function");
+ assert(typeof q === "function");
+ assert(typeof r === "function");
+ assert(typeof s === "function");
+ assert(typeof t === "function");
+ assert(typeof u === "function");
+ assert(typeof v === "function");
+ assert(typeof w === "function");
+ assert(typeof x === "function");
+ assert(typeof y === "function");
+ assert(typeof z === "function");
+});
+
+for (let i = 0; i < 500; i++)
+ assert(foo() === 25);
+function foo() { return 20; }
+
+{
+ function foo() { return 25; }
+ assert(foo() === 25);
+}
+assert(foo() === 25);
+
+for (let i = 0; i < 500; i++)
+ assert(bar() === "bar2");
+function bar() { return "bar1"; }
+if (falsey()) {
+ {
+ if (falsey()) {
+ function bar() { return "bar2"; }
+ }
+ }
+}
+assert(bar() === "bar2");
+
+for (let i = 0; i < 500; i++)
+ assert(baz() === "baz2");
+function baz() { return "baz1"; }
+while (falsey()) {
+ if (falsey()) {
+ function baz() { return "baz2"; }
+ }
+}
+assert(baz() === "baz2");
</ins></span></pre>
</div>
</div>
</body>
</html>