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);
+}