Title: [197915] trunk
Revision
197915
Author
[email protected]
Date
2016-03-09 18:04:20 -0800 (Wed, 09 Mar 2016)

Log Message

ES6: Implement lexical scoping for function definitions in strict mode
https://bugs.webkit.org/show_bug.cgi?id=152844

Reviewed by Geoffrey Garen.

Source/_javascript_Core:

This patch implements block scoping for function definitions
in strict mode. The implementation works as follows:
        
- If we're in sloppy mode, function declarations work exactly
  as they did before this patch. I.e, function declarations are hoisted
  and declared like "var" variables.
        
- If you're in strict mode and at the top of a function scope or program
  scope, function declarations still work like they used to. They are defined
  like "var" variables. This is necessary for backwards compatibility
  because ES5 strict mode allowed duplicate function declarations at the
  top-most scope of a program/function.
        
- If you're in strict mode and inside a block statement or a switch statement,
  function declarations are now block scoped. All function declarations within
  a block are hoisted to the beginning of the block. They are not hoisted out of the 
  block like they are in sloppy mode. This allows for the following types of
  programs:
  ```
  function foo() {
      function bar() { return 20; }
      {
          function bar() { return 30; }
          bar(); // 30
      }
      bar(); // 20
  }
  ```

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::instantiateLexicalVariables):
(JSC::BytecodeGenerator::emitPrefillStackTDZVariables):
(JSC::BytecodeGenerator::pushLexicalScope):
(JSC::BytecodeGenerator::pushLexicalScopeInternal):
(JSC::BytecodeGenerator::initializeBlockScopedFunctions):
(JSC::BytecodeGenerator::popLexicalScope):
(JSC::BytecodeGenerator::liftTDZCheckIfPossible):
(JSC::BytecodeGenerator::pushTDZVariables):
(JSC::BytecodeGenerator::getVariablesUnderTDZ):
(JSC::BytecodeGenerator::emitNewRegExp):
(JSC::BytecodeGenerator::emitNewFunctionExpressionCommon):
(JSC::BytecodeGenerator::emitNewFunctionExpression):
(JSC::BytecodeGenerator::emitNewArrowFunctionExpression):
* bytecompiler/BytecodeGenerator.h:
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createSourceElements):
(JSC::ASTBuilder::features):
(JSC::ASTBuilder::numConstants):
(JSC::ASTBuilder::createFuncDeclStatement):
(JSC::ASTBuilder::createClassDeclStatement):
(JSC::ASTBuilder::createBlockStatement):
(JSC::ASTBuilder::createTryStatement):
(JSC::ASTBuilder::createSwitchStatement):
(JSC::ASTBuilder::Scope::Scope):
(JSC::ASTBuilder::funcDeclarations): Deleted.
* parser/NodeConstructors.h:
(JSC::CaseBlockNode::CaseBlockNode):
(JSC::SwitchNode::SwitchNode):
(JSC::BlockNode::BlockNode):
* parser/Nodes.cpp:
(JSC::ScopeNode::ScopeNode):
(JSC::ScopeNode::singleStatement):
(JSC::ProgramNode::ProgramNode):
(JSC::ModuleProgramNode::ModuleProgramNode):
(JSC::EvalNode::EvalNode):
(JSC::FunctionNode::FunctionNode):
(JSC::VariableEnvironmentNode::VariableEnvironmentNode):
* parser/Nodes.h:
(JSC::VariableEnvironmentNode::VariableEnvironmentNode):
(JSC::VariableEnvironmentNode::lexicalVariables):
(JSC::VariableEnvironmentNode::functionStack):
(JSC::ScopeNode::captures):
(JSC::ScopeNode::varDeclarations):
(JSC::ScopeNode::neededConstants):
(JSC::ProgramNode::startColumn):
(JSC::ProgramNode::endColumn):
(JSC::EvalNode::startColumn):
(JSC::EvalNode::endColumn):
(JSC::ModuleProgramNode::startColumn):
(JSC::ModuleProgramNode::endColumn):
(JSC::ScopeNode::functionStack): Deleted.
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::didFinishParsing):
(JSC::Parser<LexerType>::parseStatementListItem):
(JSC::Parser<LexerType>::parseSwitchStatement):
(JSC::Parser<LexerType>::parseBlockStatement):
(JSC::Parser<LexerType>::parseStatement):
(JSC::Parser<LexerType>::parseFunctionInfo):
(JSC::getMetadata):
(JSC::Parser<LexerType>::parseFunctionDeclaration):
(JSC::Parser<LexerType>::parseExportDeclaration):
* parser/Parser.h:
(JSC::Scope::declareVariable):
(JSC::Scope::declareFunction):
(JSC::Scope::appendFunction):
(JSC::Scope::takeFunctionDeclarations):
(JSC::Scope::declareLexicalVariable):
(JSC::Parser::currentVariableScope):
(JSC::Parser::currentLexicalDeclarationScope):
(JSC::Parser::currentFunctionScope):
(JSC::Parser::pushScope):
(JSC::Parser::popScopeInternal):
(JSC::Parser::declareVariable):
(JSC::Parser::declareFunction):
(JSC::Parser::hasDeclaredVariable):
(JSC::Parser::isFunctionMetadataNode):
(JSC::Parser<LexerType>::parse):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::createFuncDeclStatement):
(JSC::SyntaxChecker::createClassDeclStatement):
(JSC::SyntaxChecker::createBlockStatement):
(JSC::SyntaxChecker::createExprStatement):
(JSC::SyntaxChecker::createIfStatement):
(JSC::SyntaxChecker::createContinueStatement):
(JSC::SyntaxChecker::createTryStatement):
(JSC::SyntaxChecker::createSwitchStatement):
(JSC::SyntaxChecker::createWhileStatement):
(JSC::SyntaxChecker::createWithStatement):
(JSC::SyntaxChecker::createDoWhileStatement):
* parser/VariableEnvironment.h:
(JSC::VariableEnvironmentEntry::isExported):
(JSC::VariableEnvironmentEntry::isImported):
(JSC::VariableEnvironmentEntry::isImportedNamespace):
(JSC::VariableEnvironmentEntry::isFunction):
(JSC::VariableEnvironmentEntry::setIsCaptured):
(JSC::VariableEnvironmentEntry::setIsConst):
(JSC::VariableEnvironmentEntry::setIsExported):
(JSC::VariableEnvironmentEntry::setIsImported):
(JSC::VariableEnvironmentEntry::setIsImportedNamespace):
(JSC::VariableEnvironmentEntry::setIsFunction):
(JSC::VariableEnvironmentEntry::clearIsVar):
(JSC::VariableEnvironment::VariableEnvironment):
(JSC::VariableEnvironment::begin):
(JSC::VariableEnvironment::end):
* tests/es6.yaml:
* tests/stress/block-scoped-function-declarations.js: Added.
(assert):
(test):
(f.foo.bar):
(f.foo.):
(f.foo):
(f):
(assert.foo.):
(assert.foo):
(assert.foo.foo):
(assert.foo.bar):
(assert.foo.switch.case.1):
(assert.foo.switch.case.2):
(assert.foo.switch.foo):
(assert.foo.switch.bar):

LayoutTests:

* js/let-syntax-expected.txt:
* js/parser-syntax-check-expected.txt:
* js/script-tests/parser-syntax-check.js:
(testFailed):
(runTest):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (197914 => 197915)


--- trunk/LayoutTests/ChangeLog	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/LayoutTests/ChangeLog	2016-03-10 02:04:20 UTC (rev 197915)
@@ -1,3 +1,16 @@
+2016-03-09  Saam Barati  <[email protected]>
+
+        ES6: Implement lexical scoping for function definitions in strict mode
+        https://bugs.webkit.org/show_bug.cgi?id=152844
+
+        Reviewed by Geoffrey Garen.
+
+        * js/let-syntax-expected.txt:
+        * js/parser-syntax-check-expected.txt:
+        * js/script-tests/parser-syntax-check.js:
+        (testFailed):
+        (runTest):
+
 2016-03-09  Jer Noble  <[email protected]>
 
         Add heuristic for "main content" videos which override user gesture requirements

Modified: trunk/LayoutTests/js/let-syntax-expected.txt (197914 => 197915)


--- trunk/LayoutTests/js/let-syntax-expected.txt	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/LayoutTests/js/let-syntax-expected.txt	2016-03-10 02:04:20 UTC (rev 197915)
@@ -315,11 +315,11 @@
 SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
 SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
 PASS Has syntax error: ''use strict'; function f() { let x; var [x] = 20; }'
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
 PASS Has syntax error: 'function f() { let x;  function x(){} }'
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
 PASS Has syntax error: ''use strict'; function f() { let x;  function x(){} }'
 SyntaxError: Cannot declare a let variable twice: 'x'.
 SyntaxError: Cannot declare a let variable twice: 'x'.
@@ -345,11 +345,11 @@
 SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
 SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
 PASS Has syntax error: ''use strict'; function f() { const x = 20; var [x] = 20; }'
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
 PASS Has syntax error: 'function f() { const x = 20;  function x(){} }'
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
 PASS Has syntax error: ''use strict'; function f() { const x = 20;  function x(){} }'
 SyntaxError: Cannot declare a const variable twice: 'x'.
 SyntaxError: Cannot declare a const variable twice: 'x'.
@@ -375,11 +375,11 @@
 SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
 SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
 PASS Has syntax error: ''use strict'; function f() { class x{}; var [x] = 20; }'
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
 PASS Has syntax error: 'function f() { class x{};  function x(){} }'
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
-SyntaxError: Cannot declare a function that shadows a let/const/class variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
+SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
 PASS Has syntax error: ''use strict'; function f() { class x{};  function x(){} }'
 SyntaxError: Cannot declare a class twice: 'x'.
 SyntaxError: Cannot declare a class twice: 'x'.

Modified: trunk/LayoutTests/js/parser-syntax-check-expected.txt (197914 => 197915)


--- trunk/LayoutTests/js/parser-syntax-check-expected.txt	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/LayoutTests/js/parser-syntax-check-expected.txt	2016-03-10 02:04:20 UTC (rev 197915)
@@ -628,18 +628,56 @@
 PASS Valid:   "function f() { 'use strict'; function f1(a) { function f2(b) { return b; } return f2(a); } f1(5); }"
 PASS Valid:   "'use strict'; function f1(a) { function f2(b) { function f3(c) { return c; } return f3(b); } return f2(a); } f1(5);"
 PASS Valid:   "function f() { 'use strict'; function f1(a) { function f2(b) { function f3(c) { return c; } return f3(b); } return f2(a); } f1(5); }"
-PASS Invalid: "'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } f1(5);"
-PASS Invalid: "function f() { 'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } f1(5); }"
-PASS Invalid: "'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } f1(5);"
-PASS Invalid: "function f() { 'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } f1(5); }"
+PASS Valid:   "'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } f1(5);"
+PASS Valid:   "function f() { 'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } f1(5); }"
+PASS Valid:   "'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } f1(5);"
+PASS Valid:   "function f() { 'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } f1(5); }"
+PASS Valid:   "'use strict'; function f1(a) {}; function f1(a) {};"
+PASS Valid:   "function f() { 'use strict'; function f1(a) {}; function f1(a) {}; }"
+PASS Invalid: "'use strict'; { function f1(a) {}; function f1(a) {}; }"
+PASS Invalid: "function f() { 'use strict'; { function f1(a) {}; function f1(a) {}; } }"
+PASS Invalid: "'use strict'; { let f1; function f1(a) {}; }"
+PASS Invalid: "function f() { 'use strict'; { let f1; function f1(a) {}; } }"
+PASS Invalid: "'use strict'; { function f1(a) {}; let f1; }"
+PASS Invalid: "function f() { 'use strict'; { function f1(a) {}; let f1; } }"
+PASS Invalid: "'use strict'; let f1; function f1(a) {};"
+PASS Invalid: "function f() { 'use strict'; let f1; function f1(a) {}; }"
+PASS Invalid: "'use strict'; function f1(a) {}; let f1; "
+PASS Invalid: "function f() { 'use strict'; function f1(a) {}; let f1;  }"
+PASS Invalid: "let f1; function f1(a) {};"
+PASS Invalid: "function f() { let f1; function f1(a) {}; }"
+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; } }"
+PASS Valid:   "switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }"
+PASS Valid:   "function f() { switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; } }"
+PASS Invalid: "'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }"
+PASS Invalid: "function f() { 'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; } }"
+PASS Invalid: "'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: let foo; break; }"
+PASS Invalid: "function f() { 'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: let foo; break; } }"
+PASS Invalid: "'use strict'; switch('foo') { case 1: let foo; break; case 2: function foo() {}; break; }"
+PASS Invalid: "function f() { 'use strict'; switch('foo') { case 1: let foo; break; case 2: function foo() {}; break; } }"
+PASS Valid:   "'use strict'; switch('foo') { case 1: { let foo; break; } case 2: function foo() {}; break; }"
+PASS Valid:   "function f() { 'use strict'; switch('foo') { case 1: { let foo; break; } case 2: function foo() {}; break; } }"
+PASS Valid:   "'use strict'; switch('foo') { case 1: { function foo() { }; break; } case 2: function foo() {}; break; }"
+PASS Valid:   "function f() { 'use strict'; switch('foo') { case 1: { function foo() { }; break; } case 2: function foo() {}; break; } }"
+PASS Invalid: "'use strict'; if (true) function foo() { }; "
+PASS Invalid: "function f() { 'use strict'; if (true) function foo() { };  }"
+PASS Valid:   "if (true) function foo() { }; "
+PASS Valid:   "function f() { if (true) function foo() { };  }"
 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);"
 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); }"
 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);"
 PASS Valid:   "function f() { 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); }"
-PASS Invalid: "var str = "'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } return f1(arguments[0]);"; var foo = new Function(str); foo(5);"
-FAIL Invalid: "function f() { var str = "'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } return f1(arguments[0]);"; var foo = new Function(str); foo(5); }" but did not throw
-PASS Invalid: "var str = "'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } return f1(arguments[0]);"; var foo = new Function(str); foo(5);"
-FAIL Invalid: "function f() { var str = "'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } return f1(arguments[0]);"; var foo = new Function(str); foo(5); }" but did not throw
+PASS Valid:   "var str = "'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } return f1(arguments[0]);"; var foo = new Function(str); foo(5);"
+PASS Valid:   "function f() { var str = "'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } return f1(arguments[0]);"; var foo = new Function(str); foo(5); }"
+PASS Valid:   "var str = "'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } return f1(arguments[0]);"; var foo = new Function(str); foo(5);"
+PASS Valid:   "function f() { var str = "'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } return f1(arguments[0]);"; var foo = new Function(str); foo(5); }"
 PASS Valid:   "if (0) $foo; "
 PASS Valid:   "function f() { if (0) $foo;  }"
 PASS Valid:   "if (0) _foo; "

Modified: trunk/LayoutTests/js/script-tests/parser-syntax-check.js (197914 => 197915)


--- trunk/LayoutTests/js/script-tests/parser-syntax-check.js	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/LayoutTests/js/script-tests/parser-syntax-check.js	2016-03-10 02:04:20 UTC (rev 197915)
@@ -2,6 +2,11 @@
 "This test checks that the following expressions or statements are valid ECMASCRIPT code or should throw parse error"
 );
 
+function testFailed(msg) {
+    description(msg);
+    throw new Error("Bad!");
+}
+
 function runTest(_a, expectSyntaxError)
 {
     var error;
@@ -399,13 +404,32 @@
 
 valid("'use strict'; function f1(a) { function f2(b) { return b; } return f2(a); } f1(5);")
 valid("'use strict'; function f1(a) { function f2(b) { function f3(c) { return c; } return f3(b); } return f2(a); } f1(5);")
-invalid("'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } f1(5);")
-invalid("'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } f1(5);")
+valid("'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } f1(5);")
+valid("'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } f1(5);")
+valid("'use strict'; function f1(a) {}; function f1(a) {};")
+invalid("'use strict'; { function f1(a) {}; function f1(a) {}; }")
+invalid("'use strict'; { let f1; function f1(a) {}; }")
+invalid("'use strict'; { function f1(a) {}; let f1; }")
+invalid("'use strict'; let f1; function f1(a) {};")
+invalid("'use strict'; function f1(a) {}; let f1; ")
+invalid("let f1; function f1(a) {};")
+invalid("let f1; { function f1(a) {}; }")
+invalid("{ function f1(a) {}; } let f1;")
+valid("{ let f1; function f1(a) {}; }")
+valid("{  function f1(a) {}; let f1; }")
+valid("switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }")
+invalid("'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }");
+invalid("'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: let foo; break; }");
+invalid("'use strict'; switch('foo') { case 1: let foo; break; case 2: function foo() {}; break; }");
+valid("'use strict'; switch('foo') { case 1: { let foo; break; } case 2: function foo() {}; break; }");
+valid("'use strict'; switch('foo') { case 1: { function foo() { }; break; } case 2: function foo() {}; break; }");
+invalid("'use strict'; if (true) function foo() { }; ");
+valid("if (true) function foo() { }; ");
 
 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);")
 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);")
-invalid("var str = \"'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } return f1(arguments[0]);\"; var foo = new Function(str); foo(5);", SyntaxError, undefined)
-invalid("var str = \"'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } return f1(arguments[0]);\"; var foo = new Function(str); foo(5);", SyntaxError, undefined)
+valid("var str = \"'use strict'; function f1(a) { if (a) { function f2(b) { return b; } return f2(a); } else return a; } return f1(arguments[0]);\"; var foo = new Function(str); foo(5);")
+valid("var str = \"'use strict'; function f1(a) { function f2(b) { if (b) { function f3(c) { return c; } return f3(b); } else return b; } return f2(a); } return f1(arguments[0]);\"; var foo = new Function(str); foo(5);")
 
 valid("if (0) $foo; ")
 valid("if (0) _foo; ")

Modified: trunk/Source/_javascript_Core/ChangeLog (197914 => 197915)


--- trunk/Source/_javascript_Core/ChangeLog	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-03-10 02:04:20 UTC (rev 197915)
@@ -1,3 +1,163 @@
+2016-03-09  Saam Barati  <[email protected]>
+
+        ES6: Implement lexical scoping for function definitions in strict mode
+        https://bugs.webkit.org/show_bug.cgi?id=152844
+
+        Reviewed by Geoffrey Garen.
+
+        This patch implements block scoping for function definitions
+        in strict mode. The implementation works as follows:
+        
+        - If we're in sloppy mode, function declarations work exactly
+          as they did before this patch. I.e, function declarations are hoisted
+          and declared like "var" variables.
+        
+        - If you're in strict mode and at the top of a function scope or program
+          scope, function declarations still work like they used to. They are defined
+          like "var" variables. This is necessary for backwards compatibility
+          because ES5 strict mode allowed duplicate function declarations at the
+          top-most scope of a program/function.
+        
+        - If you're in strict mode and inside a block statement or a switch statement,
+          function declarations are now block scoped. All function declarations within
+          a block are hoisted to the beginning of the block. They are not hoisted out of the 
+          block like they are in sloppy mode. This allows for the following types of
+          programs:
+          ```
+          function foo() {
+              function bar() { return 20; }
+              {
+                  function bar() { return 30; }
+                  bar(); // 30
+              }
+              bar(); // 20
+          }
+          ```
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        (JSC::BytecodeGenerator::instantiateLexicalVariables):
+        (JSC::BytecodeGenerator::emitPrefillStackTDZVariables):
+        (JSC::BytecodeGenerator::pushLexicalScope):
+        (JSC::BytecodeGenerator::pushLexicalScopeInternal):
+        (JSC::BytecodeGenerator::initializeBlockScopedFunctions):
+        (JSC::BytecodeGenerator::popLexicalScope):
+        (JSC::BytecodeGenerator::liftTDZCheckIfPossible):
+        (JSC::BytecodeGenerator::pushTDZVariables):
+        (JSC::BytecodeGenerator::getVariablesUnderTDZ):
+        (JSC::BytecodeGenerator::emitNewRegExp):
+        (JSC::BytecodeGenerator::emitNewFunctionExpressionCommon):
+        (JSC::BytecodeGenerator::emitNewFunctionExpression):
+        (JSC::BytecodeGenerator::emitNewArrowFunctionExpression):
+        * bytecompiler/BytecodeGenerator.h:
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::createSourceElements):
+        (JSC::ASTBuilder::features):
+        (JSC::ASTBuilder::numConstants):
+        (JSC::ASTBuilder::createFuncDeclStatement):
+        (JSC::ASTBuilder::createClassDeclStatement):
+        (JSC::ASTBuilder::createBlockStatement):
+        (JSC::ASTBuilder::createTryStatement):
+        (JSC::ASTBuilder::createSwitchStatement):
+        (JSC::ASTBuilder::Scope::Scope):
+        (JSC::ASTBuilder::funcDeclarations): Deleted.
+        * parser/NodeConstructors.h:
+        (JSC::CaseBlockNode::CaseBlockNode):
+        (JSC::SwitchNode::SwitchNode):
+        (JSC::BlockNode::BlockNode):
+        * parser/Nodes.cpp:
+        (JSC::ScopeNode::ScopeNode):
+        (JSC::ScopeNode::singleStatement):
+        (JSC::ProgramNode::ProgramNode):
+        (JSC::ModuleProgramNode::ModuleProgramNode):
+        (JSC::EvalNode::EvalNode):
+        (JSC::FunctionNode::FunctionNode):
+        (JSC::VariableEnvironmentNode::VariableEnvironmentNode):
+        * parser/Nodes.h:
+        (JSC::VariableEnvironmentNode::VariableEnvironmentNode):
+        (JSC::VariableEnvironmentNode::lexicalVariables):
+        (JSC::VariableEnvironmentNode::functionStack):
+        (JSC::ScopeNode::captures):
+        (JSC::ScopeNode::varDeclarations):
+        (JSC::ScopeNode::neededConstants):
+        (JSC::ProgramNode::startColumn):
+        (JSC::ProgramNode::endColumn):
+        (JSC::EvalNode::startColumn):
+        (JSC::EvalNode::endColumn):
+        (JSC::ModuleProgramNode::startColumn):
+        (JSC::ModuleProgramNode::endColumn):
+        (JSC::ScopeNode::functionStack): Deleted.
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseInner):
+        (JSC::Parser<LexerType>::didFinishParsing):
+        (JSC::Parser<LexerType>::parseStatementListItem):
+        (JSC::Parser<LexerType>::parseSwitchStatement):
+        (JSC::Parser<LexerType>::parseBlockStatement):
+        (JSC::Parser<LexerType>::parseStatement):
+        (JSC::Parser<LexerType>::parseFunctionInfo):
+        (JSC::getMetadata):
+        (JSC::Parser<LexerType>::parseFunctionDeclaration):
+        (JSC::Parser<LexerType>::parseExportDeclaration):
+        * parser/Parser.h:
+        (JSC::Scope::declareVariable):
+        (JSC::Scope::declareFunction):
+        (JSC::Scope::appendFunction):
+        (JSC::Scope::takeFunctionDeclarations):
+        (JSC::Scope::declareLexicalVariable):
+        (JSC::Parser::currentVariableScope):
+        (JSC::Parser::currentLexicalDeclarationScope):
+        (JSC::Parser::currentFunctionScope):
+        (JSC::Parser::pushScope):
+        (JSC::Parser::popScopeInternal):
+        (JSC::Parser::declareVariable):
+        (JSC::Parser::declareFunction):
+        (JSC::Parser::hasDeclaredVariable):
+        (JSC::Parser::isFunctionMetadataNode):
+        (JSC::Parser<LexerType>::parse):
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::createFuncDeclStatement):
+        (JSC::SyntaxChecker::createClassDeclStatement):
+        (JSC::SyntaxChecker::createBlockStatement):
+        (JSC::SyntaxChecker::createExprStatement):
+        (JSC::SyntaxChecker::createIfStatement):
+        (JSC::SyntaxChecker::createContinueStatement):
+        (JSC::SyntaxChecker::createTryStatement):
+        (JSC::SyntaxChecker::createSwitchStatement):
+        (JSC::SyntaxChecker::createWhileStatement):
+        (JSC::SyntaxChecker::createWithStatement):
+        (JSC::SyntaxChecker::createDoWhileStatement):
+        * parser/VariableEnvironment.h:
+        (JSC::VariableEnvironmentEntry::isExported):
+        (JSC::VariableEnvironmentEntry::isImported):
+        (JSC::VariableEnvironmentEntry::isImportedNamespace):
+        (JSC::VariableEnvironmentEntry::isFunction):
+        (JSC::VariableEnvironmentEntry::setIsCaptured):
+        (JSC::VariableEnvironmentEntry::setIsConst):
+        (JSC::VariableEnvironmentEntry::setIsExported):
+        (JSC::VariableEnvironmentEntry::setIsImported):
+        (JSC::VariableEnvironmentEntry::setIsImportedNamespace):
+        (JSC::VariableEnvironmentEntry::setIsFunction):
+        (JSC::VariableEnvironmentEntry::clearIsVar):
+        (JSC::VariableEnvironment::VariableEnvironment):
+        (JSC::VariableEnvironment::begin):
+        (JSC::VariableEnvironment::end):
+        * tests/es6.yaml:
+        * tests/stress/block-scoped-function-declarations.js: Added.
+        (assert):
+        (test):
+        (f.foo.bar):
+        (f.foo.):
+        (f.foo):
+        (f):
+        (assert.foo.):
+        (assert.foo):
+        (assert.foo.foo):
+        (assert.foo.bar):
+        (assert.foo.switch.case.1):
+        (assert.foo.switch.case.2):
+        (assert.foo.switch.foo):
+        (assert.foo.switch.bar):
+
 2016-03-09  Saam barati  <[email protected]>
 
         Array.isArray support for Proxy

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (197914 => 197915)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2016-03-10 02:04:20 UTC (rev 197915)
@@ -566,7 +566,7 @@
 
     // All "addVar()"s needs to happen before "initializeDefaultParameterValuesAndSetupFunctionScopeStack()" is called
     // because a function's default parameter ExpressionNodes will use temporary registers.
-    m_TDZStack.append(std::make_pair(*parentScopeTDZVariables, false));
+    pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize);
     initializeDefaultParameterValuesAndSetupFunctionScopeStack(parameters, functionNode, functionSymbolTable, symbolTableConstantIndex, captures);
     
     // Loading |this| inside an arrow function must be done after initializeDefaultParameterValuesAndSetupFunctionScopeStack()
@@ -582,7 +582,8 @@
         emitPutDerivedConstructorToArrowFunctionContextScope();
     }
 
-    pushLexicalScope(m_scopeNode, TDZCheckOptimization::Optimize);
+    bool shouldInitializeBlockScopedFunctions = false; // We generate top-level function declarations in ::generate().
+    pushLexicalScope(m_scopeNode, TDZCheckOptimization::Optimize, NestedScopeType::IsNotNested, nullptr, shouldInitializeBlockScopedFunctions);
 }
 
 BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode, const VariableEnvironment* parentScopeTDZVariables)
@@ -623,7 +624,7 @@
     }
     codeBlock->adoptVariables(variables);
 
-    m_TDZStack.append(std::make_pair(*parentScopeTDZVariables, false));
+    pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize);
 
     if (codeBlock->isArrowFunctionContext() && evalNode->usesThis())
         emitLoadThisFromArrowFunctionLexicalEnvironment();
@@ -633,7 +634,8 @@
         emitPutThisToArrowFunctionContextScope();
     }
     
-    pushLexicalScope(m_scopeNode, TDZCheckOptimization::Optimize);
+    bool shouldInitializeBlockScopedFunctions = false; // We generate top-level function declarations in ::generate().
+    pushLexicalScope(m_scopeNode, TDZCheckOptimization::Optimize, NestedScopeType::IsNotNested, nullptr, shouldInitializeBlockScopedFunctions);
 }
 
 BytecodeGenerator::BytecodeGenerator(VM& vm, ModuleProgramNode* moduleProgramNode, UnlinkedModuleProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode, const VariableEnvironment* parentScopeTDZVariables)
@@ -715,8 +717,9 @@
     else
         constantSymbolTable = addConstantValue(moduleEnvironmentSymbolTable->cloneScopePart(*m_vm));
 
-    m_TDZStack.append(std::make_pair(lexicalVariables, true));
-    m_symbolTableStack.append(SymbolTableStackEntry { Strong<SymbolTable>(*m_vm, moduleEnvironmentSymbolTable), m_topMostScope, false, constantSymbolTable->index() });
+    pushTDZVariables(lexicalVariables, TDZCheckOptimization::Optimize);
+    bool isWithScope = false;
+    m_symbolTableStack.append(SymbolTableStackEntry { Strong<SymbolTable>(*m_vm, moduleEnvironmentSymbolTable), m_topMostScope, isWithScope, constantSymbolTable->index() });
     emitPrefillStackTDZVariables(lexicalVariables, moduleEnvironmentSymbolTable);
 
     // makeFunction assumes that there's correct TDZ stack entries.
@@ -1742,7 +1745,7 @@
     {
         ConcurrentJITLocker locker(symbolTable->m_lock);
         for (auto& entry : lexicalVariables) {
-            ASSERT(entry.value.isLet() || entry.value.isConst());
+            ASSERT(entry.value.isLet() || entry.value.isConst() || entry.value.isFunction());
             ASSERT(!entry.value.isVar());
             SymbolTableEntry symbolTableEntry = symbolTable->get(locker, entry.key.get());
             ASSERT(symbolTableEntry.isNull());
@@ -1789,6 +1792,9 @@
         if (entry.value.isImported() && !entry.value.isImportedNamespace())
             continue;
 
+        if (entry.value.isFunction())
+            continue;
+
         SymbolTableEntry symbolTableEntry = symbolTable->get(entry.key.get());
         ASSERT(!symbolTableEntry.isNull());
         VarOffset offset = symbolTableEntry.varOffset();
@@ -1800,10 +1806,17 @@
     }
 }
 
-void BytecodeGenerator::pushLexicalScope(VariableEnvironmentNode* node, TDZCheckOptimization tdzCheckOptimization, NestedScopeType nestedScopeType, RegisterID** constantSymbolTableResult)
+void BytecodeGenerator::pushLexicalScope(VariableEnvironmentNode* node, TDZCheckOptimization tdzCheckOptimization, NestedScopeType nestedScopeType, RegisterID** constantSymbolTableResult, bool shouldInitializeBlockScopedFunctions)
 {
     VariableEnvironment& environment = node->lexicalVariables();
-    pushLexicalScopeInternal(environment, tdzCheckOptimization, nestedScopeType, constantSymbolTableResult, TDZRequirement::UnderTDZ, ScopeType::LetConstScope, ScopeRegisterType::Block);
+    RegisterID* constantSymbolTableResultTemp = nullptr;
+    pushLexicalScopeInternal(environment, tdzCheckOptimization, nestedScopeType, &constantSymbolTableResultTemp, TDZRequirement::UnderTDZ, ScopeType::LetConstScope, ScopeRegisterType::Block);
+
+    if (shouldInitializeBlockScopedFunctions)
+        initializeBlockScopedFunctions(environment, node->functionStack(), constantSymbolTableResultTemp);
+
+    if (constantSymbolTableResult && constantSymbolTableResultTemp)
+        *constantSymbolTableResult = constantSymbolTableResultTemp;
 }
 
 void BytecodeGenerator::pushLexicalScopeInternal(VariableEnvironment& environment, TDZCheckOptimization tdzCheckOptimization, NestedScopeType nestedScopeType,
@@ -1869,15 +1882,69 @@
         pushScopedControlFlowContext();
     }
 
-    m_symbolTableStack.append(SymbolTableStackEntry{ symbolTable, newScope, false, symbolTableConstantIndex });
-    bool canOptimizeTDZChecks = tdzCheckOptimization == TDZCheckOptimization::Optimize;
+    bool isWithScope = false;
+    m_symbolTableStack.append(SymbolTableStackEntry{ symbolTable, newScope, isWithScope, symbolTableConstantIndex });
     if (tdzRequirement == TDZRequirement::UnderTDZ)
-        m_TDZStack.append(std::make_pair(environment, canOptimizeTDZChecks));
+        pushTDZVariables(environment, tdzCheckOptimization);
 
     if (tdzRequirement == TDZRequirement::UnderTDZ)
         emitPrefillStackTDZVariables(environment, symbolTable.get());
 }
 
+void BytecodeGenerator::initializeBlockScopedFunctions(VariableEnvironment& environment, FunctionStack& functionStack, RegisterID* constantSymbolTable)
+{
+    /*
+     * We must transform block scoped function declarations in strict mode like so:
+     *
+     * function foo() {
+     *     if (c) {
+     *           function foo() { ... }
+     *           if (bar) { ... }
+     *           else { ... }
+     *           function baz() { ... }
+     *     }
+     * }
+     *
+     * to:
+     *
+     * function foo() {
+     *     if (c) {
+     *         let foo = function foo() { ... }
+     *         let baz = function baz() { ... }
+     *         if (bar) { ... }
+     *         else { ... }
+     *     }
+     * }
+     * 
+     * But without the TDZ checks.
+    */
+
+    if (!environment.size()) {
+        RELEASE_ASSERT(!functionStack.size());
+        return;
+    }
+
+    if (!functionStack.size())
+        return;
+
+    SymbolTable* symbolTable = m_symbolTableStack.last().m_symbolTable.get();
+    RegisterID* scope = m_symbolTableStack.last().m_scope;
+    RefPtr<RegisterID> temp = newTemporary();
+    int symbolTableIndex = constantSymbolTable ? constantSymbolTable->index() : 0;
+    for (FunctionMetadataNode* function : functionStack) {
+        const Identifier& name = function->ident();
+        auto iter = environment.find(name.impl());
+        RELEASE_ASSERT(iter != environment.end());
+        RELEASE_ASSERT(iter->value.isFunction());
+        // We purposefully don't hold the symbol table lock around this loop because emitNewFunctionExpressionCommon may GC.
+        SymbolTableEntry entry = symbolTable->get(name.impl()); 
+        RELEASE_ASSERT(!entry.isNull());
+        emitNewFunctionExpressionCommon(temp.get(), function);
+        bool isLexicallyScoped = true;
+        emitPutToScope(scope, variableForLocalEntry(name, entry, symbolTableIndex, isLexicallyScoped), temp.get(), DoNotThrowIfNotFound, Initialization);
+    }
+}
+
 void BytecodeGenerator::popLexicalScope(VariableEnvironmentNode* node)
 {
     VariableEnvironment& environment = node->lexicalVariables();
@@ -2553,8 +2620,8 @@
     for (unsigned i = m_TDZStack.size(); i--;) {
         VariableEnvironment& environment = m_TDZStack[i].first;
         if (environment.contains(identifier)) {
-            bool isSyntacticallyAbleToOptimizeTDZ = m_TDZStack[i].second;
-            if (isSyntacticallyAbleToOptimizeTDZ) {
+            TDZCheckOptimization tdzCheckOptimizationCapability = m_TDZStack[i].second;
+            if (tdzCheckOptimizationCapability == TDZCheckOptimization::Optimize) {
                 bool wasRemoved = environment.remove(identifier);
                 RELEASE_ASSERT(wasRemoved);
             }
@@ -2563,6 +2630,23 @@
     }
 }
 
+void BytecodeGenerator::pushTDZVariables(VariableEnvironment environment, TDZCheckOptimization optimization)
+{
+    if (!environment.size())
+        return;
+
+    Vector<UniquedStringImpl*, 4> functionsToRemove;
+    for (const auto& entry : environment) {
+        if (entry.value.isFunction())
+            functionsToRemove.append(entry.key.get());
+    }
+
+    for (UniquedStringImpl* function : functionsToRemove)
+        environment.remove(function);
+
+    m_TDZStack.append(std::make_pair(WTFMove(environment), optimization));
+}
+
 void BytecodeGenerator::getVariablesUnderTDZ(VariableEnvironment& result)
 {
     for (auto& pair : m_TDZStack) {
@@ -2683,9 +2767,8 @@
     return dst;
 }
 
-void BytecodeGenerator::emitNewFunctionExpressionCommon(RegisterID* dst, BaseFuncExprNode* func)
+void BytecodeGenerator::emitNewFunctionExpressionCommon(RegisterID* dst, FunctionMetadataNode* function)
 {
-    FunctionMetadataNode* function = func->metadata();
     unsigned index = m_codeBlock->addFunctionExpr(makeFunction(function));
 
     OpcodeID opcodeID = op_new_func_exp;
@@ -2711,14 +2794,14 @@
 
 RegisterID* BytecodeGenerator::emitNewFunctionExpression(RegisterID* dst, FuncExprNode* func)
 {
-    emitNewFunctionExpressionCommon(dst, func);
+    emitNewFunctionExpressionCommon(dst, func->metadata());
     return dst;
 }
 
 RegisterID* BytecodeGenerator::emitNewArrowFunctionExpression(RegisterID* dst, ArrowFuncExprNode* func)
 {
     ASSERT(func->metadata()->parseMode() == SourceParseMode::ArrowFunctionMode);    
-    emitNewFunctionExpressionCommon(dst, func);
+    emitNewFunctionExpressionCommon(dst, func->metadata());
     return dst;
 }
 

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (197914 => 197915)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2016-03-10 02:04:20 UTC (rev 197915)
@@ -709,6 +709,7 @@
         enum class ScopeType { CatchScope, LetConstScope, FunctionNameScope };
         enum class ScopeRegisterType { Var, Block };
         void pushLexicalScopeInternal(VariableEnvironment&, TDZCheckOptimization, NestedScopeType, RegisterID** constantSymbolTableResult, TDZRequirement, ScopeType, ScopeRegisterType);
+        void initializeBlockScopedFunctions(VariableEnvironment&, FunctionStack&, RegisterID* constantSymbolTable);
         void popLexicalScopeInternal(VariableEnvironment&, TDZRequirement);
         template<typename LookUpVarKindFunctor>
         bool instantiateLexicalVariables(const VariableEnvironment&, SymbolTable*, ScopeRegisterType, LookUpVarKindFunctor);
@@ -716,7 +717,7 @@
         void emitPopScope(RegisterID* dst, RegisterID* scope);
         RegisterID* emitGetParentScope(RegisterID* dst, RegisterID* scope);
         void emitPushFunctionNameScope(const Identifier& property, RegisterID* value, bool isCaptured);
-        void emitNewFunctionExpressionCommon(RegisterID*, BaseFuncExprNode*);
+        void emitNewFunctionExpressionCommon(RegisterID*, FunctionMetadataNode*);
         
         bool isNewTargetUsedInInnerArrowFunction();
         bool isSuperUsedInInnerArrowFunction();
@@ -725,7 +726,7 @@
     public:
         bool isSuperCallUsedInInnerArrowFunction();
         bool isThisUsedInInnerArrowFunction();
-        void pushLexicalScope(VariableEnvironmentNode*, TDZCheckOptimization, NestedScopeType = NestedScopeType::IsNotNested, RegisterID** constantSymbolTableResult = nullptr);
+        void pushLexicalScope(VariableEnvironmentNode*, TDZCheckOptimization, NestedScopeType = NestedScopeType::IsNotNested, RegisterID** constantSymbolTableResult = nullptr, bool shouldInitializeBlockScopedFunctions = true);
         void popLexicalScope(VariableEnvironmentNode*);
         void prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode*, RegisterID* loopSymbolTable);
         int labelScopeDepth() const;
@@ -859,7 +860,8 @@
             int m_symbolTableConstantIndex;
         };
         Vector<SymbolTableStackEntry> m_symbolTableStack;
-        Vector<std::pair<VariableEnvironment, bool>> m_TDZStack;
+        Vector<std::pair<VariableEnvironment, TDZCheckOptimization>> m_TDZStack;
+        void pushTDZVariables(VariableEnvironment, TDZCheckOptimization);
 
         ScopeNode* const m_scopeNode;
         Strong<UnlinkedCodeBlock> m_codeBlock;

Modified: trunk/Source/_javascript_Core/parser/ASTBuilder.h (197914 => 197915)


--- trunk/Source/_javascript_Core/parser/ASTBuilder.h	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/parser/ASTBuilder.h	2016-03-10 02:04:20 UTC (rev 197915)
@@ -133,7 +133,6 @@
 
     JSC::SourceElements* createSourceElements() { return new (m_parserArena) JSC::SourceElements(); }
 
-    DeclarationStacks::FunctionStack& funcDeclarations() { return m_scope.m_funcDeclarations; }
     int features() const { return m_scope.m_features; }
     int numConstants() const { return m_scope.m_numConstants; }
 
@@ -482,7 +481,6 @@
             m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn));
         if (*functionInfo.name == m_vm->propertyNames->arguments)
             usesArguments();
-        m_scope.m_funcDeclarations.append(decl->metadata());
         functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset);
         return decl;
     }
@@ -496,9 +494,9 @@
         return decl;
     }
 
-    StatementNode* createBlockStatement(const JSTokenLocation& location, JSC::SourceElements* elements, int startLine, int endLine, VariableEnvironment& lexicalVariables)
+    StatementNode* createBlockStatement(const JSTokenLocation& location, JSC::SourceElements* elements, int startLine, int endLine, VariableEnvironment& lexicalVariables, DeclarationStacks::FunctionStack&& functionStack)
     {
-        BlockNode* block = new (m_parserArena) BlockNode(location, elements, lexicalVariables);
+        BlockNode* block = new (m_parserArena) BlockNode(location, elements, lexicalVariables, WTFMove(functionStack));
         block->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset);
         return block;
     }
@@ -628,10 +626,10 @@
         return result;
     }
 
-    StatementNode* createSwitchStatement(const JSTokenLocation& location, ExpressionNode* expr, ClauseListNode* firstClauses, CaseClauseNode* defaultClause, ClauseListNode* secondClauses, int startLine, int endLine, VariableEnvironment& lexicalVariables)
+    StatementNode* createSwitchStatement(const JSTokenLocation& location, ExpressionNode* expr, ClauseListNode* firstClauses, CaseClauseNode* defaultClause, ClauseListNode* secondClauses, int startLine, int endLine, VariableEnvironment& lexicalVariables, DeclarationStacks::FunctionStack&& functionStack)
     {
         CaseBlockNode* cases = new (m_parserArena) CaseBlockNode(firstClauses, defaultClause, secondClauses);
-        SwitchNode* result = new (m_parserArena) SwitchNode(location, expr, cases, lexicalVariables);
+        SwitchNode* result = new (m_parserArena) SwitchNode(location, expr, cases, lexicalVariables, WTFMove(functionStack));
         result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset);
         return result;
     }
@@ -935,7 +933,6 @@
             , m_numConstants(0)
         {
         }
-        DeclarationStacks::FunctionStack m_funcDeclarations;
         int m_features;
         int m_numConstants;
     };

Modified: trunk/Source/_javascript_Core/parser/NodeConstructors.h (197914 => 197915)


--- trunk/Source/_javascript_Core/parser/NodeConstructors.h	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/parser/NodeConstructors.h	2016-03-10 02:04:20 UTC (rev 197915)
@@ -964,17 +964,17 @@
     {
     }
 
-    inline SwitchNode::SwitchNode(const JSTokenLocation& location, ExpressionNode* expr, CaseBlockNode* block, VariableEnvironment& lexicalVariables)
+    inline SwitchNode::SwitchNode(const JSTokenLocation& location, ExpressionNode* expr, CaseBlockNode* block, VariableEnvironment& lexicalVariables, FunctionStack&& functionStack)
         : StatementNode(location)
-        , VariableEnvironmentNode(lexicalVariables)
+        , VariableEnvironmentNode(lexicalVariables, WTFMove(functionStack))
         , m_expr(expr)
         , m_block(block)
     {
     }
 
-    inline BlockNode::BlockNode(const JSTokenLocation& location, SourceElements* statements, VariableEnvironment& lexicalVariables)
+    inline BlockNode::BlockNode(const JSTokenLocation& location, SourceElements* statements, VariableEnvironment& lexicalVariables, FunctionStack&& functionStack)
         : StatementNode(location)
-        , VariableEnvironmentNode(lexicalVariables)
+        , VariableEnvironmentNode(lexicalVariables, WTFMove(functionStack))
         , m_statements(statements)
     {
     }

Modified: trunk/Source/_javascript_Core/parser/Nodes.cpp (197914 => 197915)


--- trunk/Source/_javascript_Core/parser/Nodes.cpp	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/parser/Nodes.cpp	2016-03-10 02:04:20 UTC (rev 197915)
@@ -94,10 +94,10 @@
 {
 }
 
-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)
+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)
     : StatementNode(endLocation)
     , ParserArenaRoot(parserArena)
-    , VariableEnvironmentNode(lexicalVariables)
+    , VariableEnvironmentNode(lexicalVariables, WTFMove(funcStack))
     , m_startLineNumber(startLocation.line)
     , m_startStartOffset(startLocation.startOffset)
     , m_startLineStartOffset(startLocation.lineStartOffset)
@@ -108,7 +108,6 @@
     , m_statements(children)
 {
     m_varDeclarations.swap(varEnvironment);
-    m_functionStack.swap(funcStack);
 }
 
 StatementNode* ScopeNode::singleStatement() const
@@ -118,8 +117,8 @@
 
 // ------------------------------ ProgramNode -----------------------------
 
-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, funcStack, lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
+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)
     , m_startColumn(startColumn)
     , m_endColumn(endColumn)
 {
@@ -127,8 +126,8 @@
 
 // ------------------------------ ModuleProgramNode -----------------------------
 
-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, funcStack, lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
+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)
     , m_startColumn(startColumn)
     , m_endColumn(endColumn)
 {
@@ -136,8 +135,8 @@
 
 // ------------------------------ EvalNode -----------------------------
 
-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, funcStack, lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
+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)
     , m_endColumn(endColumn)
 {
 }
@@ -182,8 +181,8 @@
 
 // ------------------------------ FunctionNode -----------------------------
 
-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, funcStack, lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
+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)
     , m_parameters(parameters)
     , m_startColumn(startColumn)
     , m_endColumn(endColumn)
@@ -202,4 +201,10 @@
     m_lexicalVariables.swap(lexicalVariables);
 }
 
+VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables, FunctionStack&& functionStack)
+{
+    m_lexicalVariables.swap(lexicalVariables);
+    m_functionStack = WTFMove(functionStack);
+}
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/parser/Nodes.h (197914 => 197915)


--- trunk/Source/_javascript_Core/parser/Nodes.h	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/parser/Nodes.h	2016-03-10 02:04:20 UTC (rev 197915)
@@ -214,16 +214,21 @@
 
     class VariableEnvironmentNode : public ParserArenaDeletable {
     public:
+        typedef DeclarationStacks::FunctionStack FunctionStack;
+
         VariableEnvironmentNode()
         {
         }
 
         VariableEnvironmentNode(VariableEnvironment& lexicalDeclaredVariables);
+        VariableEnvironmentNode(VariableEnvironment& lexicalDeclaredVariables, FunctionStack&&);
 
         VariableEnvironment& lexicalVariables() { return m_lexicalVariables; }
+        FunctionStack& functionStack() { return m_functionStack; }
 
     protected:
         VariableEnvironment m_lexicalVariables;
+        FunctionStack m_functionStack;
     };
 
     class ConstantNode : public ExpressionNode {
@@ -1303,7 +1308,7 @@
     public:
         using ParserArenaDeletable::operator new;
 
-        BlockNode(const JSTokenLocation&, SourceElements*, VariableEnvironment&);
+        BlockNode(const JSTokenLocation&, SourceElements*, VariableEnvironment&, FunctionStack&&);
 
         StatementNode* singleStatement() const;
         StatementNode* lastStatement() const;
@@ -1551,10 +1556,9 @@
 
     class ScopeNode : public StatementNode, public ParserArenaRoot, public VariableEnvironmentNode {
     public:
-        typedef DeclarationStacks::FunctionStack FunctionStack;
 
         ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, bool inStrictContext);
-        ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, const SourceCode&, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
+        ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, const SourceCode&, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
 
         using ParserArenaRoot::operator new;
 
@@ -1593,7 +1597,6 @@
         bool captures(const Identifier& ident) { return captures(ident.impl()); }
 
         VariableEnvironment& varDeclarations() { return m_varDeclarations; }
-        FunctionStack& functionStack() { return m_functionStack; }
 
         int neededConstants()
         {
@@ -1618,14 +1621,13 @@
         InnerArrowFunctionCodeFeatures m_innerArrowFunctionCodeFeatures;
         SourceCode m_source;
         VariableEnvironment m_varDeclarations;
-        FunctionStack m_functionStack;
         int m_numConstants;
         SourceElements* m_statements;
     };
 
     class ProgramNode : public ScopeNode {
     public:
-        ProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
+        ProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
 
         unsigned startColumn() const { return m_startColumn; }
         unsigned endColumn() const { return m_endColumn; }
@@ -1640,7 +1642,7 @@
 
     class EvalNode : public ScopeNode {
     public:
-        EvalNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
+        EvalNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
 
         ALWAYS_INLINE unsigned startColumn() const { return 0; }
         unsigned endColumn() const { return m_endColumn; }
@@ -1655,7 +1657,7 @@
 
     class ModuleProgramNode : public ScopeNode {
     public:
-        ModuleProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
+        ModuleProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
 
         unsigned startColumn() const { return m_startColumn; }
         unsigned endColumn() const { return m_endColumn; }
@@ -1897,7 +1899,7 @@
 
     class FunctionNode final : public ScopeNode {
     public:
-        FunctionNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
+        FunctionNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
 
         FunctionParameters* parameters() const { return m_parameters; }
 
@@ -2203,7 +2205,7 @@
     public:
         using ParserArenaDeletable::operator new;
 
-        SwitchNode(const JSTokenLocation&, ExpressionNode*, CaseBlockNode*, VariableEnvironment&);
+        SwitchNode(const JSTokenLocation&, ExpressionNode*, CaseBlockNode*, VariableEnvironment&, FunctionStack&&);
 
     private:
         void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;

Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (197914 => 197915)


--- trunk/Source/_javascript_Core/parser/Parser.cpp	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp	2016-03-10 02:04:20 UTC (rev 197915)
@@ -336,17 +336,17 @@
         }
     }
 #endif // NDEBUG
-    didFinishParsing(sourceElements, context.funcDeclarations(), varDeclarations, features, context.numConstants());
+    didFinishParsing(sourceElements, scope->takeFunctionDeclarations(), varDeclarations, features, context.numConstants());
 
     return parseError;
 }
 
 template <typename LexerType>
-void Parser<LexerType>::didFinishParsing(SourceElements* sourceElements, DeclarationStacks::FunctionStack& funcStack, 
+void Parser<LexerType>::didFinishParsing(SourceElements* sourceElements, DeclarationStacks::FunctionStack&& funcStack, 
     VariableEnvironment& varDeclarations, CodeFeatures features, int numConstants)
 {
     m_sourceElements = sourceElements;
-    m_funcDeclarations.swap(funcStack);
+    m_funcDeclarations = WTFMove(funcStack);
     m_varDeclarations.swap(varDeclarations);
     m_features = features;
     m_numConstants = numConstants;
@@ -571,6 +571,9 @@
         result = parseClassDeclaration(context);
         break;
 #endif
+    case FUNCTION:
+        result = parseFunctionDeclaration(context);
+        break;
     default:
         m_statementDepth--; // parseStatement() increments the depth.
         result = parseStatement(context, directive, directiveLiteralLength);
@@ -1424,7 +1427,7 @@
     endSwitch();
     handleProductionOrFail(CLOSEBRACE, "}", "end", "body of a 'switch'");
     
-    TreeStatement result = context.createSwitchStatement(location, expr, firstClauses, defaultClause, secondClauses, startLine, endLine, lexicalScope->finalizeLexicalEnvironment());
+    TreeStatement result = context.createSwitchStatement(location, expr, firstClauses, defaultClause, secondClauses, startLine, endLine, lexicalScope->finalizeLexicalEnvironment(), lexicalScope->takeFunctionDeclarations());
     popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo);
     return result;
 }
@@ -1562,11 +1565,12 @@
     int startOffset = m_token.m_data.offset;
     int start = tokenLine();
     VariableEnvironment emptyEnvironment;
+    DeclarationStacks::FunctionStack emptyFunctionStack;
     next();
     if (match(CLOSEBRACE)) {
         int endOffset = m_token.m_data.offset;
         next();
-        TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line, shouldPushLexicalScope ? currentScope()->finalizeLexicalEnvironment() : emptyEnvironment);
+        TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line, shouldPushLexicalScope ? currentScope()->finalizeLexicalEnvironment() : emptyEnvironment, shouldPushLexicalScope ? currentScope()->takeFunctionDeclarations() : WTFMove(emptyFunctionStack));
         context.setStartOffset(result, startOffset);
         context.setEndOffset(result, endOffset);
         if (shouldPushLexicalScope)
@@ -1578,7 +1582,7 @@
     matchOrFail(CLOSEBRACE, "Expected a closing '}' at the end of a block statement");
     int endOffset = m_token.m_data.offset;
     next();
-    TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line, shouldPushLexicalScope ? currentScope()->finalizeLexicalEnvironment() : emptyEnvironment);
+    TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line, shouldPushLexicalScope ? currentScope()->finalizeLexicalEnvironment() : emptyEnvironment, shouldPushLexicalScope ? currentScope()->takeFunctionDeclarations() : WTFMove(emptyFunctionStack));
     context.setStartOffset(result, startOffset);
     context.setEndOffset(result, endOffset);
     if (shouldPushLexicalScope)
@@ -1607,8 +1611,10 @@
         result = parseVariableDeclaration(context, DeclarationType::VarDeclaration);
         break;
     case FUNCTION:
-        failIfFalseIfStrict(m_statementDepth == 1, "Strict mode does not allow function declarations in a lexically nested statement");
-        result = parseFunctionDeclaration(context);
+        if (!strictMode())
+            result = parseFunctionDeclaration(context);
+        else
+            failWithMessage("Function declarations are only allowed inside blocks or switch statements in strict mode");
         break;
     case SEMICOLON: {
         JSTokenLocation location(tokenLocation());
@@ -2121,6 +2127,9 @@
     return true;
 }
 
+static NO_RETURN_DUE_TO_CRASH FunctionMetadataNode* getMetadata(ParserFunctionInfo<SyntaxChecker>&) { RELEASE_ASSERT_NOT_REACHED(); }
+static FunctionMetadataNode* getMetadata(ParserFunctionInfo<ASTBuilder>& info) { return info.body; }
+
 template <typename LexerType>
 template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context, ExportType exportType)
 {
@@ -2137,15 +2146,20 @@
     failIfFalse((parseFunctionInfo(context, FunctionNeedsName, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration)), "Cannot parse this function");
     failIfFalse(functionInfo.name, "Function statements must have a name");
 
-    DeclarationResultMask declarationResult = declareVariable(functionInfo.name);
+    std::pair<DeclarationResultMask, ScopeRef> functionDeclaration = declareFunction(functionInfo.name);
+    DeclarationResultMask declarationResult = functionDeclaration.first;
     failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare a function named '", functionInfo.name->impl(), "' in strict mode");
     if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration)
-        internalFailWithMessage(false, "Cannot declare a function that shadows a let/const/class variable '", functionInfo.name->impl(), "' in strict mode");
+        internalFailWithMessage(false, "Cannot declare a function that shadows a let/const/class/function variable '", functionInfo.name->impl(), "' in strict mode");
     if (exportType == ExportType::Exported) {
         semanticFailIfFalse(exportName(*functionInfo.name), "Cannot export a duplicate function name: '", functionInfo.name->impl(), "'");
         currentScope()->moduleScopeData().exportBinding(*functionInfo.name);
     }
-    return context.createFuncDeclStatement(location, functionInfo);
+
+    TreeStatement result = context.createFuncDeclStatement(location, functionInfo);
+    if (TreeBuilder::CreatesAST)
+        functionDeclaration.second->appendFunction(getMetadata(functionInfo));
+    return result;
 }
 
 template <typename LexerType>
@@ -2767,8 +2781,11 @@
         }
 
         if (localName) {
-            if (match(FUNCTION))
+            if (match(FUNCTION)) {
+                DepthManager statementDepth(&m_statementDepth);
+                m_statementDepth = 1;
                 result = parseFunctionDeclaration(context);
+            }
 #if ENABLE(ES6_CLASS_SYNTAX)
             else {
                 ASSERT(match(CLASSTOKEN));
@@ -2878,9 +2895,12 @@
             result = parseVariableDeclaration(context, DeclarationType::LetDeclaration, ExportType::Exported);
             break;
 
-        case FUNCTION:
+        case FUNCTION: {
+            DepthManager statementDepth(&m_statementDepth);
+            m_statementDepth = 1;
             result = parseFunctionDeclaration(context, ExportType::Exported);
             break;
+        }
 
 #if ENABLE(ES6_CLASS_SYNTAX)
         case CLASSTOKEN:

Modified: trunk/Source/_javascript_Core/parser/Parser.h (197914 => 197915)


--- trunk/Source/_javascript_Core/parser/Parser.h	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/parser/Parser.h	2016-03-10 02:04:20 UTC (rev 197915)
@@ -368,6 +368,37 @@
         return result;
     }
 
+    DeclarationResultMask declareFunction(const Identifier* ident, bool declareAsVar)
+    {
+        ASSERT(m_allowsVarDeclarations || m_allowsLexicalDeclarations);
+        DeclarationResultMask result = DeclarationResult::Valid;
+        bool isValidStrictMode = !isEvalOrArgumentsIdentifier(m_vm, ident);
+        if (!isValidStrictMode)
+            result |= DeclarationResult::InvalidStrictMode;
+        m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
+        auto addResult = declareAsVar ? m_declaredVariables.add(ident->impl()) : m_lexicalVariables.add(ident->impl());
+        addResult.iterator->value.setIsFunction();
+        if (declareAsVar) {
+            addResult.iterator->value.setIsVar();
+            if (m_lexicalVariables.contains(ident->impl()))
+                result |= DeclarationResult::InvalidDuplicateDeclaration;
+        } else {
+            addResult.iterator->value.setIsLet();
+            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.");
+            if (!addResult.isNewEntry) 
+                result |= DeclarationResult::InvalidDuplicateDeclaration;
+        }
+        return result;
+    }
+
+    void appendFunction(FunctionMetadataNode* node)
+    { 
+        ASSERT(node);
+        m_functionDeclarations.append(node);
+    }
+    DeclarationStacks::FunctionStack&& takeFunctionDeclarations() { return WTFMove(m_functionDeclarations); }
+    
+
     DeclarationResultMask declareLexicalVariable(const Identifier* ident, bool isConstant, DeclarationImportType importType = DeclarationImportType::NotImported)
     {
         ASSERT(m_allowsLexicalDeclarations);
@@ -691,6 +722,7 @@
     IdentifierSet m_closedVariableCandidates;
     IdentifierSet m_writtenVariables;
     RefPtr<ModuleScopeData> m_moduleScopeData { };
+    DeclarationStacks::FunctionStack m_functionDeclarations;
 };
 
 typedef Vector<Scope, 10> ScopeStack;
@@ -923,6 +955,18 @@
         return ScopeRef(&m_scopeStack, i);
     }
 
+    ScopeRef currentLexicalDeclarationScope()
+    {
+        unsigned i = m_scopeStack.size() - 1;
+        ASSERT(i < m_scopeStack.size());
+        while (!m_scopeStack[i].allowsLexicalDeclarations()) {
+            i--;
+            ASSERT(i < m_scopeStack.size());
+        }
+
+        return ScopeRef(&m_scopeStack, i);
+    }
+
     ScopeRef currentFunctionScope()
     {
         unsigned i = m_scopeStack.size() - 1;
@@ -959,7 +1003,7 @@
         m_scopeStack.append(Scope(m_vm, isFunction, isGenerator, isStrict, isArrowFunction));
         return currentScope();
     }
-    
+
     void popScopeInternal(ScopeRef& scope, bool shouldTrackClosedVariables)
     {
         ASSERT_UNUSED(scope, scope.index() == m_scopeStack.size() - 1);
@@ -1001,22 +1045,30 @@
         if (type == DeclarationType::VarDeclaration)
             return currentVariableScope()->declareVariable(ident);
 
-        unsigned i = m_scopeStack.size() - 1;
-        ASSERT(i < m_scopeStack.size());
         ASSERT(type == DeclarationType::LetDeclaration || type == DeclarationType::ConstDeclaration);
-
         // Lexical variables declared at a top level scope that shadow arguments or vars are not allowed.
         if (m_statementDepth == 1 && (hasDeclaredParameter(*ident) || hasDeclaredVariable(*ident)))
             return DeclarationResult::InvalidDuplicateDeclaration;
 
-        while (!m_scopeStack[i].allowsLexicalDeclarations()) {
-            i--;
-            ASSERT(i < m_scopeStack.size());
+        return currentLexicalDeclarationScope()->declareLexicalVariable(ident, type == DeclarationType::ConstDeclaration, importType);
+    }
+
+    std::pair<DeclarationResultMask, ScopeRef> declareFunction(const Identifier* ident)
+    {
+        if (m_statementDepth == 1 || !strictMode()) {
+            // Functions declared at the top-most scope (both in sloppy and strict mode) are declared as vars
+            // for backwards compatibility. This allows us to declare functions with the same name more than once.
+            // In sloppy mode, we always declare functions as vars.
+            bool declareAsVar = true;
+            ScopeRef variableScope = currentVariableScope();
+            return std::make_pair(variableScope->declareFunction(ident, declareAsVar), variableScope);
         }
 
-        return m_scopeStack[i].declareLexicalVariable(ident, type == DeclarationType::ConstDeclaration, importType);
+        bool declareAsVar = false;
+        ScopeRef lexicalVariableScope = currentLexicalDeclarationScope();
+        return std::make_pair(lexicalVariableScope->declareFunction(ident, declareAsVar), lexicalVariableScope);
     }
-    
+
     NEVER_INLINE bool hasDeclaredVariable(const Identifier& ident)
     {
         unsigned i = m_scopeStack.size() - 1;
@@ -1061,7 +1113,7 @@
     Parser();
     String parseInner(const Identifier&, SourceParseMode);
 
-    void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&, VariableEnvironment&, CodeFeatures, int);
+    void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&&, VariableEnvironment&, CodeFeatures, int);
 
     // Used to determine type of error to report.
     bool isFunctionMetadataNode(ScopeNode*) { return false; }
@@ -1543,7 +1595,7 @@
                                     endColumn,
                                     m_sourceElements,
                                     m_varDeclarations,
-                                    m_funcDeclarations,
+                                    WTFMove(m_funcDeclarations),
                                     currentScope()->finalizeLexicalEnvironment(),
                                     m_parameters,
                                     *m_source,

Modified: trunk/Source/_javascript_Core/parser/SyntaxChecker.h (197914 => 197915)


--- trunk/Source/_javascript_Core/parser/SyntaxChecker.h	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/parser/SyntaxChecker.h	2016-03-10 02:04:20 UTC (rev 197915)
@@ -31,7 +31,7 @@
 #include "YarrSyntaxChecker.h"
 
 namespace JSC {
-    
+
 class SyntaxChecker {
 public:
     struct BinaryExprContext {
@@ -230,7 +230,7 @@
     int createFuncDeclStatement(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return StatementResult; }
     int createClassDeclStatement(const JSTokenLocation&, ClassExpression,
         const JSTextPosition&, const JSTextPosition&, int, int) { return StatementResult; }
-    int createBlockStatement(const JSTokenLocation&, int, int, int, VariableEnvironment&) { return StatementResult; }
+    int createBlockStatement(const JSTokenLocation&, int, int, int, VariableEnvironment&, DeclarationStacks::FunctionStack&&) { return StatementResult; }
     int createExprStatement(const JSTokenLocation&, int, int, int) { return StatementResult; }
     int createIfStatement(const JSTokenLocation&, int, int, int, int) { return StatementResult; }
     int createIfStatement(const JSTokenLocation&, int, int, int, int, int) { return StatementResult; }
@@ -245,7 +245,7 @@
     int createContinueStatement(const JSTokenLocation&, int, int) { return StatementResult; }
     int createContinueStatement(const JSTokenLocation&, const Identifier*, int, int) { return StatementResult; }
     int createTryStatement(const JSTokenLocation&, int, int, int, int, int, int, VariableEnvironment&) { return StatementResult; }
-    int createSwitchStatement(const JSTokenLocation&, int, int, int, int, int, int, VariableEnvironment&) { return StatementResult; }
+    int createSwitchStatement(const JSTokenLocation&, int, int, int, int, int, int, VariableEnvironment&, DeclarationStacks::FunctionStack&&) { return StatementResult; }
     int createWhileStatement(const JSTokenLocation&, int, int, int, int) { return StatementResult; }
     int createWithStatement(const JSTokenLocation&, int, int, int, int, int, int) { return StatementResult; }
     int createDoWhileStatement(const JSTokenLocation&, int, int, int, int) { return StatementResult; }

Modified: trunk/Source/_javascript_Core/parser/VariableEnvironment.h (197914 => 197915)


--- trunk/Source/_javascript_Core/parser/VariableEnvironment.h	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/parser/VariableEnvironment.h	2016-03-10 02:04:20 UTC (rev 197915)
@@ -40,6 +40,7 @@
     ALWAYS_INLINE bool isExported() const { return m_bits & IsExported; }
     ALWAYS_INLINE bool isImported() const { return m_bits & IsImported; }
     ALWAYS_INLINE bool isImportedNamespace() const { return m_bits & IsImportedNamespace; }
+    ALWAYS_INLINE bool isFunction() const { return m_bits & IsFunction; }
 
     ALWAYS_INLINE void setIsCaptured() { m_bits |= IsCaptured; }
     ALWAYS_INLINE void setIsConst() { m_bits |= IsConst; }
@@ -48,18 +49,20 @@
     ALWAYS_INLINE void setIsExported() { m_bits |= IsExported; }
     ALWAYS_INLINE void setIsImported() { m_bits |= IsImported; }
     ALWAYS_INLINE void setIsImportedNamespace() { m_bits |= IsImportedNamespace; }
+    ALWAYS_INLINE void setIsFunction() { m_bits |= IsFunction; }
 
     ALWAYS_INLINE void clearIsVar() { m_bits &= ~IsVar; }
 
 private:
-    enum Traits {
+    enum Traits : uint8_t {
         IsCaptured = 1 << 0,
         IsConst = 1 << 1,
         IsVar = 1 << 2,
         IsLet = 1 << 3,
         IsExported = 1 << 4,
         IsImported = 1 << 5,
-        IsImportedNamespace = 1 << 6
+        IsImportedNamespace = 1 << 6,
+        IsFunction = 1 << 7
     };
     uint8_t m_bits { 0 };
 };
@@ -72,6 +75,15 @@
 private:
     typedef HashMap<RefPtr<UniquedStringImpl>, VariableEnvironmentEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, VariableEnvironmentEntryHashTraits> Map;
 public:
+    VariableEnvironment()
+    { }
+    VariableEnvironment(VariableEnvironment&& other)
+        : m_map(WTFMove(other.m_map))
+        , m_isEverythingCaptured(other.m_isEverythingCaptured)
+    { }
+    VariableEnvironment(const VariableEnvironment& other) = default;
+    VariableEnvironment& operator=(const VariableEnvironment& other) = default;
+
     ALWAYS_INLINE Map::iterator begin() { return m_map.begin(); }
     ALWAYS_INLINE Map::iterator end() { return m_map.end(); }
     ALWAYS_INLINE Map::const_iterator begin() const { return m_map.begin(); }

Modified: trunk/Source/_javascript_Core/tests/es6.yaml (197914 => 197915)


--- trunk/Source/_javascript_Core/tests/es6.yaml	2016-03-10 01:55:34 UTC (rev 197914)
+++ trunk/Source/_javascript_Core/tests/es6.yaml	2016-03-10 02:04:20 UTC (rev 197915)
@@ -753,7 +753,7 @@
 - path: es6/arrow_functions_no_prototype_property.js
   cmd: runES6 :normal
 - path: es6/block-level_function_declaration.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/destructuring_computed_properties.js
   cmd: runES6 :normal
 - path: es6/destructuring_defaults_in_parameters_separate_scope.js

Added: trunk/Source/_javascript_Core/tests/stress/block-scoped-function-declarations.js (0 => 197915)


--- trunk/Source/_javascript_Core/tests/stress/block-scoped-function-declarations.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/block-scoped-function-declarations.js	2016-03-10 02:04:20 UTC (rev 197915)
@@ -0,0 +1,227 @@
+"use strict";
+
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion")
+}
+
+function test(f) {
+    for (let i = 0; i < 500; i++)
+        f();
+}
+
+{
+    let called = false;
+    function foo() {
+        called = true;
+        function bar() { return 25; }
+        assert(bar() === 25);
+        {
+            function bar() { return 30; }
+            assert(bar() === 30);
+        }
+        assert(bar() === 25);
+    }
+    test(foo);
+    assert(called);
+}
+
+{
+    let called = false;
+    function foo() {
+        called = true;
+        assert(bar() === 25);
+        {
+            assert(bar() === 30);
+            function bar() { return 30; }
+        }
+        assert(bar() === 25);
+
+        function bar() { return 25; }
+    }
+    test(foo);
+    assert(called);
+}
+
+{
+    let called = false;
+    function foo() {
+        called = true;
+        function foo() { return bar(); }
+        function bar() { return 25; }
+        assert(bar() === 25);
+        assert(foo() === 25);
+        {
+            function bar() { return 30; }
+            function foo() { return 25; }
+            assert(bar() === 30);
+            assert(foo() === 25);
+        }
+        assert(bar() === 25);
+        assert(foo() === 25);
+    }
+    test(foo);
+    assert(called);
+}
+
+{
+    let called = false;
+    function foo() {
+        called = true;
+        assert(bar() === 25);
+        assert(foo() === 25);
+        {
+            function bar() { return 30; }
+            function foo() { return 25; }
+            assert(bar() === 30);
+            assert(foo() === 25);
+        }
+        assert(bar() === 25);
+        assert(foo() === 25);
+
+        function foo() { return bar(); }
+        function bar() { return 25; }
+    }
+    test(foo);
+    assert(called);
+}
+
+{
+    let called = false;
+    function foo() {
+        called = true;
+        let isDefault = false;
+        switch ('foo') {
+        case 1:
+            function foo() { return 25; }
+            break;
+        case 2:
+            function bar() { return 30; }
+            break;
+        default:
+            isDefault = true;
+            assert(foo() === 25);
+            assert(bar() === 30);
+            break;
+        }
+        assert(isDefault);
+    }
+    test(foo);
+    assert(called);
+}
+
+{
+    let called = false;
+    function foo() {
+        called = true;
+        let is1 = false;
+        switch (1) {
+        case 1:
+            is1 = true;
+            function foo() { return 25; }
+            assert(foo() === 25);
+            assert(bar() === 30);
+            break;
+        case 2:
+            function bar() { return 30; }
+            break;
+        }
+        assert(is1);
+    }
+    test(foo);
+    assert(called);
+}
+
+{
+    let called = false;
+    function foo() {
+        called = true;
+        function foo() { return 25; }
+        function bar() { return "bar"; }
+        let is2 = false;
+        switch (2) {
+        case 1: {
+            function foo() { return 30; }
+            break;
+        }
+        case 2:
+            is2 = true;
+            function bar() { return 30; }
+            assert(bar() === 30);
+            assert(foo() === 25);
+            break;
+        }
+        assert(is2);
+        assert(bar() === "bar");
+        assert(foo() === 25);
+    }
+    test(foo);
+    assert(called);
+}
+
+{
+    let called = false;
+    function foo() {
+        called = true;
+        function foo() { return 25; }
+        function bar() { return "bar"; }
+        let capture = () => foo + "" + bar;
+        let is2 = false;
+        switch (2) {
+        case 1: {
+            function foo() { return 30; }
+            break;
+        }
+        case 2:
+            is2 = true;
+            function bar() { return 30; }
+            let capture = () => bar;
+            assert(bar() === 30);
+            assert(foo() === 25);
+            break;
+        }
+        assert(is2);
+        assert(bar() === "bar");
+        assert(foo() === 25);
+    }
+    test(foo);
+    assert(called);
+}
+
+{
+    let called = false;
+    function foo() {
+        called = true;
+        let f1;
+        let f2 = foo;
+        function foo() { }
+        if (true) {
+            f1 = foo;
+            function foo() { }
+        }
+        assert(!!f1 && !!f2);
+        assert(f1 !== f2);
+    }
+    test(foo);
+    assert(called);
+}
+
+{
+    let called = false;
+    function foo() {
+        called = true;
+        let f1;
+        let f2 = foo;
+        function foo() { }
+        let capture = () => foo;
+        if (true) {
+            f1 = foo;
+            function foo() { }
+            let capture = () => foo;
+        }
+        assert(!!f1 && !!f2);
+        assert(f1 !== f2);
+    }
+    test(foo);
+    assert(called);
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to