Changeset 198989 in webkit


Ignore:
Timestamp:
Apr 3, 2016 12:45:05 PM (8 years ago)
Author:
sbarati@apple.com
Message:

Implement Annex B.3.3 function hoisting rules for function code
https://bugs.webkit.org/show_bug.cgi?id=155672

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

The spec states that functions declared inside a function
inside a block scope are subject to the rules of Annex B.3.3:
https://tc39.github.io/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics

The rule states that functions declared in such blocks should
be local bindings of the block. If declaring the function's name
as a "var" in the function would not lead to a syntax error (i.e,
if we don't have a let/const/class variable with the same name)
and if we don't have a parameter with the same name, then we
implictly also declare the funcion name as a "var". When evaluating
the block statement we bind the hoisted "var" to be the value
of the local function binding.

There is one more thing we do for web compatibility. We allow
function declarations inside if/else statements that aren't
blocks. For such statements, we transform the code as if the
function were declared inside a block statement. For example:
function foo() { if (cond) function baz() { } }
is transformed into:
function foo() { if (cond) { function baz() { } } }

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack):
(JSC::BytecodeGenerator::initializeBlockScopedFunctions):

  • bytecompiler/BytecodeGenerator.h:
  • parser/Nodes.cpp:

(JSC::ScopeNode::ScopeNode):
(JSC::ProgramNode::ProgramNode):
(JSC::ModuleProgramNode::ModuleProgramNode):
(JSC::EvalNode::EvalNode):
(JSC::FunctionNode::FunctionNode):

  • parser/Nodes.h:

(JSC::ScopeNode::hasCapturedVariables):
(JSC::ScopeNode::captures):
(JSC::ScopeNode::hasSloppyModeHoistedFunction):
(JSC::ScopeNode::varDeclarations):
(JSC::ProgramNode::startColumn):
(JSC::ProgramNode::endColumn):
(JSC::EvalNode::startColumn):
(JSC::EvalNode::endColumn):
(JSC::ModuleProgramNode::startColumn):
(JSC::ModuleProgramNode::endColumn):

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::Parser):
(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::didFinishParsing):
(JSC::Parser<LexerType>::parseStatement):
(JSC::Parser<LexerType>::parseIfStatement):

  • parser/Parser.h:

(JSC::Scope::declareVariable):
(JSC::Scope::declareFunction):
(JSC::Scope::addSloppyModeHoistableFunctionCandidate):
(JSC::Scope::appendFunction):
(JSC::Scope::declareParameter):
(JSC::Scope::mergeInnerArrowFunctionFeatures):
(JSC::Scope::getSloppyModeHoistedFunctions):
(JSC::Scope::getCapturedVars):
(JSC::ScopeRef::containingScope):
(JSC::ScopeRef::operator==):
(JSC::ScopeRef::operator!=):
(JSC::Parser::declareFunction):
(JSC::Parser::hasDeclaredVariable):
(JSC::Parser::isFunctionMetadataNode):
(JSC::Parser::DepthManager::DepthManager):
(JSC::Parser<LexerType>::parse):

  • parser/VariableEnvironment.h:

(JSC::VariableEnvironmentEntry::isImported):
(JSC::VariableEnvironmentEntry::isImportedNamespace):
(JSC::VariableEnvironmentEntry::isFunction):
(JSC::VariableEnvironmentEntry::isParameter):
(JSC::VariableEnvironmentEntry::isSloppyModeHoistingCandidate):
(JSC::VariableEnvironmentEntry::setIsCaptured):
(JSC::VariableEnvironmentEntry::setIsConst):
(JSC::VariableEnvironmentEntry::setIsImported):
(JSC::VariableEnvironmentEntry::setIsImportedNamespace):
(JSC::VariableEnvironmentEntry::setIsFunction):
(JSC::VariableEnvironmentEntry::setIsParameter):
(JSC::VariableEnvironmentEntry::setIsSloppyModeHoistingCandidate):
(JSC::VariableEnvironmentEntry::clearIsVar):

  • runtime/CodeCache.h:

(JSC::SourceCodeValue::SourceCodeValue):

  • runtime/JSScope.cpp:
  • runtime/JSScope.h:
  • tests/es6.yaml:
  • tests/stress/sloppy-mode-function-hoisting.js: Added.

(assert):
(test):
(falsey):
(truthy):
(test.):
(test.a):
(test.f):
(test.let.funcs.f):
(test.catch.f):
(test.foo):
(test.bar):
(test.switch.case.0):
(test.else.f):
(test.b):
(test.c):
(test.d):
(test.e):
(test.g):
(test.h):
(test.i):
(test.j):
(test.k):
(test.l):
(test.m):
(test.n):
(test.o):
(test.p):
(test.q):
(test.r):
(test.s):
(test.t):
(test.u):
(test.v):
(test.w):
(test.x):
(test.y):
(test.z):
(foo):
(bar):
(falsey.bar):
(baz):
(falsey.baz):

LayoutTests:

  • js/kde/func-decl-expected.txt:
  • js/kde/script-tests/func-decl.js:
  • js/parser-syntax-check-expected.txt:
  • js/script-tests/parser-syntax-check.js:

(valid):
(onlyValidGlobally):
(onlyInvalidGlobally):
(invalid):

Location:
trunk
Files:
1 added
19 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r198982 r198989  
     12016-04-03  Saam barati  <sbarati@apple.com>
     2
     3        Implement Annex B.3.3 function hoisting rules for function code
     4        https://bugs.webkit.org/show_bug.cgi?id=155672
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        * js/kde/func-decl-expected.txt:
     9        * js/kde/script-tests/func-decl.js:
     10        * js/parser-syntax-check-expected.txt:
     11        * js/script-tests/parser-syntax-check.js:
     12        (valid):
     13        (onlyValidGlobally):
     14        (onlyInvalidGlobally):
     15        (invalid):
     16
    1172016-04-03  David Kilzer  <ddkilzer@apple.com>
    218
  • trunk/LayoutTests/js/function-declaration-statement-expected.txt

    r38747 r198989  
    66PASS ifTest() is true
    77PASS ifElseTest() is true
    8 PASS doWhileTest() is true
    9 PASS whileTest() is true
    10 PASS forTest() is true
    11 PASS forVarTest() is true
    12 PASS forInTest() is true
    13 PASS forInVarTest() is true
    14 PASS forInVarInitTest() is true
    15 PASS withTest() is true
    168PASS labelTest() is true
    179PASS successfullyParsed is true
  • trunk/LayoutTests/js/kde/func-decl-expected.txt

    r27724 r198989  
    44
    55
    6 PASS Function declaration takes effect at entry
    7 PASS Decl not yet overwritten
     6PASS Function declaration takes effect at declaration block
     7PASS Decl already overwritten
    88PASS After assign (0)
    99PASS function decls have no execution content
  • trunk/LayoutTests/js/kde/script-tests/func-decl.js

    r98407 r198989  
    1818function test() {
    1919  try {
    20     shouldBeOfType("Function declaration takes effect at entry", f, "function");
     20    shouldBeOfType("Function declaration takes effect at declaration block", f, "undefined");
    2121  }
    2222  catch (e) {
     
    2525
    2626  for (var i = 0; i < 3; ++i) {
    27     if (i == 0)
    28       shouldBeOfType("Decl not yet overwritten", f, 'function');
    29     else
    30       shouldBeOfType("Decl already overwritten", f, 'number');
     27    shouldBeOfType("Decl already overwritten", f, 'function');
    3128
    3229    f = 3;
  • trunk/LayoutTests/js/parser-syntax-check-expected.txt

    r198928 r198989  
    303303PASS Valid:   "if (a) function f() {} else function g() {}"
    304304PASS Valid:   "function f() { if (a) function f() {} else function g() {} }"
    305 PASS Valid:   "if (a()) while(0) function f() {} else function g() {}" with TypeError
    306 PASS Valid:  "function f() { if (a()) while(0) function f() {} else function g() {} }"
     305PASS Invalid: "if (a()) while(0) function f() {} else function g() {}"
     306PASS Invalid: "function f() { if (a()) while(0) function f() {} else function g() {} }"
    307307PASS Invalid: "if (a()) function f() { else function g() }"
    308308PASS Invalid: "function f() { if (a()) function f() { else function g() } }"
     
    677677PASS Invalid: "let f1; function f1(a) {};"
    678678PASS Invalid: "function f() { let f1; function f1(a) {}; }"
    679 PASS Invalid: "let f1; { function f1(a) {}; }"
    680 PASS Invalid: "function f() { let f1; { function f1(a) {}; } }"
    681 PASS Invalid: "{ function f1(a) {}; } let f1;"
    682 PASS Invalid: "function f() { { function f1(a) {}; } let f1; }"
    683 PASS Valid:   "{ let f1; function f1(a) {}; }"
    684 PASS Valid:   "function f() { { let f1; function f1(a) {}; } }"
    685 PASS Valid:   "{  function f1(a) {}; let f1; }"
    686 PASS Valid:   "function f() { {  function f1(a) {}; let f1; } }"
     679PASS Valid:   "function foo() { let f1; { function f1(a) {}; } }"
     680PASS Valid:   "function f() { function foo() { let f1; { function f1(a) {}; } } }"
     681PASS Valid:   "function foo() { { function f1(a) {}; } let f1; }"
     682PASS Valid:   "function f() { function foo() { { function f1(a) {}; } let f1; } }"
     683PASS Valid:   "function foo() { { function foo() { }; function foo() { } } }"
     684PASS Valid:   "function f() { function foo() { { function foo() { }; function foo() { } } } }"
     685PASS Invalid: "function foo() { 'use strict'; { function foo() { }; function foo() { } } }"
     686PASS Invalid: "function f() { function foo() { 'use strict'; { function foo() { }; function foo() { } } } }"
     687PASS Invalid: "function foo() { let f1; function f1(a) {}; }"
     688PASS Invalid: "function f() { function foo() { let f1; function f1(a) {}; } }"
     689PASS Invalid: "let f1; function f1(a) {};"
     690PASS Invalid: "function f() { let f1; function f1(a) {}; }"
     691PASS Valid:   "{ function f1(a) {}; let f1; }"
     692PASS Invalid: "function f() { { function f1(a) {}; let f1; } }"
     693PASS Valid:   "{ function f1(a) {}; const f1 = 25; }"
     694PASS Invalid: "function f() { { function f1(a) {}; const f1 = 25; } }"
     695PASS Valid:   "{ function f1(a) {}; class f1{}; }"
     696PASS Invalid: "function f() { { function f1(a) {}; class f1{}; } }"
     697PASS Invalid: "function foo() { { let bar; function bar() { } } }"
     698PASS Invalid: "function f() { function foo() { { let bar; function bar() { } } } }"
     699PASS Invalid: "function foo() { { function bar() { }; let bar; } }"
     700PASS Invalid: "function f() { function foo() { { function bar() { }; let bar; } } }"
     701PASS Invalid: "function foo() { { const bar; function bar() { } } }"
     702PASS Invalid: "function f() { function foo() { { const bar; function bar() { } } } }"
     703PASS Invalid: "function foo() { { function bar() { }; const bar; } }"
     704PASS Invalid: "function f() { function foo() { { function bar() { }; const bar; } } }"
     705PASS Invalid: "function foo() { { class bar{}; function bar() { } } }"
     706PASS Invalid: "function f() { function foo() { { class bar{}; function bar() { } } } }"
     707PASS Invalid: "function foo() { { function bar() { }; class bar{}; } }"
     708PASS Invalid: "function f() { function foo() { { function bar() { }; class bar{}; } } }"
    687709PASS Valid:   "switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }"
    688710PASS Valid:   "function f() { switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; } }"
     711PASS Valid:   "switch('foo') { case 1: let foo; function foo() {}; break; case 2: function foo() {}; break; }"
     712PASS Invalid: "function f() { switch('foo') { case 1: let foo; function foo() {}; break; case 2: function foo() {}; break; } }"
     713PASS Valid:   "switch('foo') { case 1: function foo() {}; let foo; break; case 2: function foo() {}; break; }"
     714PASS Invalid: "function f() { switch('foo') { case 1: function foo() {}; let foo; break; case 2: function foo() {}; break; } }"
     715PASS Valid:   "switch('foo') { case 1: function foo() {}; const foo = 25; break; case 2: function foo() {}; break; }"
     716PASS Invalid: "function f() { switch('foo') { case 1: function foo() {}; const foo = 25; break; case 2: function foo() {}; break; } }"
     717PASS Valid:   "switch('foo') { case 1: function foo() {}; class foo {} ; break; case 2: function foo() {}; break; }"
     718PASS Invalid: "function f() { switch('foo') { case 1: function foo() {}; class foo {} ; break; case 2: function foo() {}; break; } }"
     719PASS Valid:   "switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: let foo; }"
     720PASS Invalid: "function f() { switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: let foo; } }"
     721PASS Valid:   "function foo() { switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: { let foo; } } }"
     722PASS Valid:   "function f() { function foo() { switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: { let foo; } } } }"
    689723PASS Invalid: "'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }"
    690724PASS Invalid: "function f() { 'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; } }"
     
    701735PASS Valid:   "if (true) function foo() { }; "
    702736PASS Valid:   "function f() { if (true) function foo() { };  }"
     737PASS Invalid: " let foo; if (true) function foo() { };"
     738PASS Valid:   "function f() {  let foo; if (true) function foo() { }; }"
     739PASS Valid:   "function baz() { let foo; if (true) function foo() { }; }"
     740PASS Valid:   "function f() { function baz() { let foo; if (true) function foo() { }; } }"
     741PASS Invalid: "if (true) function foo() { }; let foo;"
     742PASS Valid:   "function f() { if (true) function foo() { }; let foo; }"
     743PASS Invalid: "{ if (true) function foo() { }; } let foo;"
     744PASS Valid:   "function f() { { if (true) function foo() { }; } let foo; }"
     745PASS Invalid: "let foo; while (false) function foo() { }; "
     746PASS Invalid: "function f() { let foo; while (false) function foo() { };  }"
     747PASS Invalid: "let foo;  { while (false) function foo() { }; } "
     748PASS Invalid: "function f() { let foo;  { while (false) function foo() { }; }  }"
     749PASS Invalid: "while (false) function foo() { }; let foo;"
     750PASS Invalid: "function f() { while (false) function foo() { }; let foo; }"
     751PASS Invalid: "let foo; while (false) label: function foo() { }; "
     752PASS Invalid: "function f() { let foo; while (false) label: function foo() { };  }"
     753PASS Invalid: "while (false) label: function foo() { }; let foo;"
     754PASS Invalid: "function f() { while (false) label: function foo() { }; let foo; }"
     755PASS Invalid: "'use strict'; while (false) function foo() { }; "
     756PASS Invalid: "function f() { 'use strict'; while (false) function foo() { };  }"
     757PASS Invalid: "'use strict'; if (false) function foo() { }; "
     758PASS Invalid: "function f() { 'use strict'; if (false) function foo() { };  }"
     759PASS Invalid: "'use strict'; do function foo() { } while (false); "
     760PASS Invalid: "function f() { 'use strict'; do function foo() { } while (false);  }"
     761PASS Invalid: "while (false) function foo() { }; "
     762PASS Invalid: "function f() { while (false) function foo() { };  }"
     763PASS Valid:   "if (false) function foo() { }; "
     764PASS Valid:   "function f() { if (false) function foo() { };  }"
     765PASS Invalid: "do function foo() { } while (false); "
     766PASS Invalid: "function f() { do function foo() { } while (false);  }"
     767PASS Invalid: "if (cond) label: function foo() { }"
     768PASS Invalid: "function f() { if (cond) label: function foo() { } }"
     769PASS Invalid: "while (true) { while (true) function bar() { } }"
     770PASS Invalid: "function f() { while (true) { while (true) function bar() { } } }"
     771PASS Invalid: "with ({}) function bar() { }"
     772PASS Invalid: "function f() { with ({}) function bar() { } }"
     773PASS Valid:   "function bar() { label: function baz() { } }"
     774PASS Valid:   "function f() { function bar() { label: function baz() { } } }"
     775PASS Valid:   "function bar() { let: function baz() { } }"
     776PASS Valid:   "function f() { function bar() { let: function baz() { } } }"
     777PASS Invalid: "function bar() { 'use strict'; let: function baz() { } }"
     778PASS Invalid: "function f() { function bar() { 'use strict'; let: function baz() { } } }"
     779PASS Valid:   "function bar() { yield: function baz() { } }"
     780PASS Valid:   "function f() { function bar() { yield: function baz() { } } }"
     781PASS Valid:   "function bar() { label: label2: function baz() { } }"
     782PASS Valid:   "function f() { function bar() { label: label2: function baz() { } } }"
     783PASS Valid:   "function bar() { label: label2: label3: function baz() { } }"
     784PASS Valid:   "function f() { function bar() { label: label2: label3: function baz() { } } }"
     785PASS Invalid: "function bar() { label: label2: label weird: function baz() { } }"
     786PASS Invalid: "function f() { function bar() { label: label2: label weird: function baz() { } } }"
     787PASS Valid:   "function bar() { label: label2: label3: function baz() { } }"
     788PASS Valid:   "function f() { function bar() { label: label2: label3: function baz() { } } }"
     789PASS Invalid: "function bar() { 'use strict'; label: label2: label 3: function baz() { } }"
     790PASS Invalid: "function f() { function bar() { 'use strict'; label: label2: label 3: function baz() { } } }"
     791PASS Invalid: "function bar() { if (cond) label: function foo() { } }"
     792PASS Invalid: "function f() { function bar() { if (cond) label: function foo() { } } }"
     793PASS Invalid: "function bar() { while (cond) label: function foo() { } }"
     794PASS Invalid: "function f() { function bar() { while (cond) label: function foo() { } } }"
     795PASS Valid:   "label: function foo() { }"
     796PASS Valid:   "function f() { label: function foo() { } }"
     797PASS Valid:   "let: function foo() { }"
     798PASS Valid:   "function f() { let: function foo() { } }"
     799PASS Valid:   "yield: function foo() { }"
     800PASS Valid:   "function f() { yield: function foo() { } }"
     801PASS Valid:   "yield: let: function foo() { }"
     802PASS Valid:   "function f() { yield: let: function foo() { } }"
     803PASS Invalid: "'use strict'; yield: let: function foo() { }"
     804PASS Invalid: "function f() { 'use strict'; yield: let: function foo() { } }"
    703805PASS 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);"
    704806PASS 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); }"
  • trunk/LayoutTests/js/script-tests/function-declaration-statement.js

    r155724 r198989  
    3636shouldBeTrue("ifElseTest()");
    3737
    38 function doWhileTest()
    39 {
    40     var i = 0;
    41     do
    42         function f()
    43         {
    44             return true;
    45         }
    46     while (i++ < 10)
    47 
    48     return f();
    49 }
    50 
    51 shouldBeTrue("doWhileTest()");
    52 
    53 function whileTest()
    54 {
    55     var i = 0;
    56     while (i++ < 10)
    57         function f()
    58         {
    59             return true;
    60         }
    61 
    62     return f();
    63 }
    64 
    65 shouldBeTrue("whileTest()");
    66 
    67 function forTest()
    68 {
    69     var i;
    70     for (i = 0; i < 10; ++i)
    71         function f()
    72         {
    73             return true;
    74         }
    75 
    76     return f();
    77 }
    78 
    79 shouldBeTrue("forTest()");
    80 
    81 function forVarTest()
    82 {
    83     for (var i = 0; i < 10; ++i)
    84         function f()
    85         {
    86             return true;
    87         }
    88 
    89     return f();
    90 }
    91 
    92 shouldBeTrue("forVarTest()");
    93 
    94 function forInTest()
    95 {
    96     var a;
    97     for (a in { field: false })
    98         function f()
    99         {
    100             return true;
    101         }
    102 
    103     return f();
    104 }
    105 
    106 shouldBeTrue("forInTest()");
    107 
    108 function forInVarTest()
    109 {
    110     var a;
    111     for (var a in { field: false })
    112         function f()
    113         {
    114             return true;
    115         }
    116 
    117     return f();
    118 }
    119 
    120 shouldBeTrue("forInVarTest()");
    121 
    122 function forInVarInitTest()
    123 {
    124     var a;
    125     for (var a in { field: false })
    126         function f()
    127         {
    128             return true;
    129         }
    130 
    131     return f();
    132 }
    133 
    134 shouldBeTrue("forInVarInitTest()");
    135 
    136 function withTest()
    137 {
    138     with ({ })
    139         function f()
    140         {
    141             return true;
    142         }
    143 
    144     return f();
    145 }
    146 
    147 shouldBeTrue("withTest()");
    148 
    14938function labelTest()
    15039{
  • trunk/LayoutTests/js/script-tests/parser-syntax-check.js

    r198928 r198989  
    4141    // Test both the grammar and the syntax checker
    4242    runTest(_a, false);
     43    runTest("function f() { " + _a + " }", false);
     44}
     45
     46function onlyValidGlobally(_a)
     47{
     48    runTest(_a, false);
     49    runTest("function f() { " + _a + " }", true);
     50}
     51
     52function onlyInvalidGlobally(_a)
     53{
     54    runTest(_a, true);
    4355    runTest("function f() { " + _a + " }", false);
    4456}
     
    227239invalid("while if (a) ;");
    228240valid  ("if (a) function f() {} else function g() {}");
    229 valid  ("if (a()) while(0) function f() {} else function g() {}");
     241invalid("if (a()) while(0) function f() {} else function g() {}");
    230242invalid("if (a()) function f() { else function g() }");
    231243invalid("if (a) if (b) ; else function f {}");
     
    429441invalid("'use strict'; function f1(a) {}; let f1; ")
    430442invalid("let f1; function f1(a) {};")
    431 invalid("let f1; { function f1(a) {}; }")
    432 invalid("{ function f1(a) {}; } let f1;")
    433 valid("{ let f1; function f1(a) {}; }")
    434 valid("{  function f1(a) {}; let f1; }")
     443valid("function foo() { let f1; { function f1(a) {}; } }")
     444valid("function foo() { { function f1(a) {}; } let f1; }")
     445valid("function foo() { { function foo() { }; function foo() { } } }")
     446invalid("function foo() { 'use strict'; { function foo() { }; function foo() { } } }")
     447invalid("function foo() { let f1; function f1(a) {}; }")
     448invalid("let f1; function f1(a) {};")
     449onlyValidGlobally("{ function f1(a) {}; let f1; }")
     450onlyValidGlobally("{ function f1(a) {}; const f1 = 25; }")
     451onlyValidGlobally("{ function f1(a) {}; class f1{}; }")
     452invalid("function foo() { { let bar; function bar() { } } }")
     453invalid("function foo() { { function bar() { }; let bar; } }")
     454invalid("function foo() { { const bar; function bar() { } } }")
     455invalid("function foo() { { function bar() { }; const bar; } }")
     456invalid("function foo() { { class bar{}; function bar() { } } }")
     457invalid("function foo() { { function bar() { }; class bar{}; } }")
    435458valid("switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }")
     459onlyValidGlobally("switch('foo') { case 1: let foo; function foo() {}; break; case 2: function foo() {}; break; }")
     460onlyValidGlobally("switch('foo') { case 1: function foo() {}; let foo; break; case 2: function foo() {}; break; }")
     461onlyValidGlobally("switch('foo') { case 1: function foo() {}; const foo = 25; break; case 2: function foo() {}; break; }")
     462onlyValidGlobally("switch('foo') { case 1: function foo() {}; class foo {} ; break; case 2: function foo() {}; break; }")
     463onlyValidGlobally("switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: let foo; }")
     464valid("function foo() { switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; case 3: { let foo; } } }")
    436465invalid("'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: function foo() {}; break; }");
    437466invalid("'use strict'; switch('foo') { case 1: function foo() {}; break; case 2: let foo; break; }");
     
    441470invalid("'use strict'; if (true) function foo() { }; ");
    442471valid("if (true) function foo() { }; ");
     472onlyInvalidGlobally(" let foo; if (true) function foo() { };");
     473valid("function baz() { let foo; if (true) function foo() { }; }");
     474onlyInvalidGlobally("if (true) function foo() { }; let foo;");
     475onlyInvalidGlobally("{ if (true) function foo() { }; } let foo;");
     476invalid("let foo; while (false) function foo() { }; ");
     477invalid("let foo;  { while (false) function foo() { }; } ");
     478invalid("while (false) function foo() { }; let foo;");
     479invalid("let foo; while (false) label: function foo() { }; ");
     480invalid("while (false) label: function foo() { }; let foo;");
     481invalid("'use strict'; while (false) function foo() { }; ");
     482invalid("'use strict'; if (false) function foo() { }; ");
     483invalid("'use strict'; do function foo() { } while (false); ");
     484invalid("while (false) function foo() { }; ");
     485valid("if (false) function foo() { }; ");
     486invalid("do function foo() { } while (false); ");
     487invalid("if (cond) label: function foo() { }");
     488invalid("while (true) { while (true) function bar() { } }");
     489invalid("with ({}) function bar() { }");
     490valid("function bar() { label: function baz() { } }");
     491valid("function bar() { let: function baz() { } }");
     492invalid("function bar() { 'use strict'; let: function baz() { } }");
     493valid("function bar() { yield: function baz() { } }");
     494valid("function bar() { label: label2: function baz() { } }");
     495valid("function bar() { label: label2: label3: function baz() { } }");
     496invalid("function bar() { label: label2: label weird: function baz() { } }");
     497valid("function bar() { label: label2: label3: function baz() { } }");
     498invalid("function bar() { 'use strict'; label: label2: label 3: function baz() { } }");
     499invalid("function bar() { if (cond) label: function foo() { } }");
     500invalid("function bar() { while (cond) label: function foo() { } }");
     501valid("label: function foo() { }");
     502valid("let: function foo() { }");
     503valid("yield: function foo() { }");
     504valid("yield: let: function foo() { }");
     505invalid("'use strict'; yield: let: function foo() { }");
    443506
    444507valid("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);")
  • trunk/Source/JavaScriptCore/ChangeLog

    r198985 r198989  
     12016-04-03  Saam barati  <sbarati@apple.com>
     2
     3        Implement Annex B.3.3 function hoisting rules for function code
     4        https://bugs.webkit.org/show_bug.cgi?id=155672
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        The spec states that functions declared inside a function
     9        inside a block scope are subject to the rules of Annex B.3.3:
     10        https://tc39.github.io/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics
     11
     12        The rule states that functions declared in such blocks should
     13        be local bindings of the block. If declaring the function's name
     14        as a "var" in the function would not lead to a syntax error (i.e,
     15        if we don't have a let/const/class variable with the same name)
     16        and if we don't have a parameter with the same name, then we
     17        implictly also declare the funcion name as a "var". When evaluating
     18        the block statement we bind the hoisted "var" to be the value
     19        of the local function binding.
     20
     21        There is one more thing we do for web compatibility. We allow
     22        function declarations inside if/else statements that aren't
     23        blocks. For such statements, we transform the code as if the
     24        function were declared inside a block statement. For example:
     25        ``` function foo() { if (cond) function baz() { } }```
     26        is transformed into:
     27        ``` function foo() { if (cond) { function baz() { } } }```
     28
     29        * bytecompiler/BytecodeGenerator.cpp:
     30        (JSC::BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack):
     31        (JSC::BytecodeGenerator::initializeBlockScopedFunctions):
     32        * bytecompiler/BytecodeGenerator.h:
     33        * parser/Nodes.cpp:
     34        (JSC::ScopeNode::ScopeNode):
     35        (JSC::ProgramNode::ProgramNode):
     36        (JSC::ModuleProgramNode::ModuleProgramNode):
     37        (JSC::EvalNode::EvalNode):
     38        (JSC::FunctionNode::FunctionNode):
     39        * parser/Nodes.h:
     40        (JSC::ScopeNode::hasCapturedVariables):
     41        (JSC::ScopeNode::captures):
     42        (JSC::ScopeNode::hasSloppyModeHoistedFunction):
     43        (JSC::ScopeNode::varDeclarations):
     44        (JSC::ProgramNode::startColumn):
     45        (JSC::ProgramNode::endColumn):
     46        (JSC::EvalNode::startColumn):
     47        (JSC::EvalNode::endColumn):
     48        (JSC::ModuleProgramNode::startColumn):
     49        (JSC::ModuleProgramNode::endColumn):
     50        * parser/Parser.cpp:
     51        (JSC::Parser<LexerType>::Parser):
     52        (JSC::Parser<LexerType>::parseInner):
     53        (JSC::Parser<LexerType>::didFinishParsing):
     54        (JSC::Parser<LexerType>::parseStatement):
     55        (JSC::Parser<LexerType>::parseIfStatement):
     56        * parser/Parser.h:
     57        (JSC::Scope::declareVariable):
     58        (JSC::Scope::declareFunction):
     59        (JSC::Scope::addSloppyModeHoistableFunctionCandidate):
     60        (JSC::Scope::appendFunction):
     61        (JSC::Scope::declareParameter):
     62        (JSC::Scope::mergeInnerArrowFunctionFeatures):
     63        (JSC::Scope::getSloppyModeHoistedFunctions):
     64        (JSC::Scope::getCapturedVars):
     65        (JSC::ScopeRef::containingScope):
     66        (JSC::ScopeRef::operator==):
     67        (JSC::ScopeRef::operator!=):
     68        (JSC::Parser::declareFunction):
     69        (JSC::Parser::hasDeclaredVariable):
     70        (JSC::Parser::isFunctionMetadataNode):
     71        (JSC::Parser::DepthManager::DepthManager):
     72        (JSC::Parser<LexerType>::parse):
     73        * parser/VariableEnvironment.h:
     74        (JSC::VariableEnvironmentEntry::isImported):
     75        (JSC::VariableEnvironmentEntry::isImportedNamespace):
     76        (JSC::VariableEnvironmentEntry::isFunction):
     77        (JSC::VariableEnvironmentEntry::isParameter):
     78        (JSC::VariableEnvironmentEntry::isSloppyModeHoistingCandidate):
     79        (JSC::VariableEnvironmentEntry::setIsCaptured):
     80        (JSC::VariableEnvironmentEntry::setIsConst):
     81        (JSC::VariableEnvironmentEntry::setIsImported):
     82        (JSC::VariableEnvironmentEntry::setIsImportedNamespace):
     83        (JSC::VariableEnvironmentEntry::setIsFunction):
     84        (JSC::VariableEnvironmentEntry::setIsParameter):
     85        (JSC::VariableEnvironmentEntry::setIsSloppyModeHoistingCandidate):
     86        (JSC::VariableEnvironmentEntry::clearIsVar):
     87        * runtime/CodeCache.h:
     88        (JSC::SourceCodeValue::SourceCodeValue):
     89        * runtime/JSScope.cpp:
     90        * runtime/JSScope.h:
     91        * tests/es6.yaml:
     92        * tests/stress/sloppy-mode-function-hoisting.js: Added.
     93        (assert):
     94        (test):
     95        (falsey):
     96        (truthy):
     97        (test.):
     98        (test.a):
     99        (test.f):
     100        (test.let.funcs.f):
     101        (test.catch.f):
     102        (test.foo):
     103        (test.bar):
     104        (test.switch.case.0):
     105        (test.else.f):
     106        (test.b):
     107        (test.c):
     108        (test.d):
     109        (test.e):
     110        (test.g):
     111        (test.h):
     112        (test.i):
     113        (test.j):
     114        (test.k):
     115        (test.l):
     116        (test.m):
     117        (test.n):
     118        (test.o):
     119        (test.p):
     120        (test.q):
     121        (test.r):
     122        (test.s):
     123        (test.t):
     124        (test.u):
     125        (test.v):
     126        (test.w):
     127        (test.x):
     128        (test.y):
     129        (test.z):
     130        (foo):
     131        (bar):
     132        (falsey.bar):
     133        (baz):
     134        (falsey.baz):
     135
    11362016-04-03  Yusuke Suzuki  <utatane.tea@gmail.com>
    2137
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r198813 r198989  
    862862    m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(*m_vm, functionSymbolTable), m_lexicalEnvironmentRegister, false, symbolTableConstantIndex });
    863863
     864    m_varScopeSymbolTableIndex = m_symbolTableStack.size() - 1;
     865
    864866    // This completes step 28 of section 9.2.12.
    865867    for (unsigned i = 0; i < valuesToMoveIntoVars.size(); i++) {
     
    19321934        bool isLexicallyScoped = true;
    19331935        emitPutToScope(scope, variableForLocalEntry(name, entry, symbolTableIndex, isLexicallyScoped), temp.get(), DoNotThrowIfNotFound, Initialization);
     1936
     1937        if (iter->value.isSloppyModeHoistingCandidate() && m_scopeNode->hasSloppyModeHoistedFunction(name.impl())) {
     1938            ASSERT(m_varScopeSymbolTableIndex);
     1939            ASSERT(*m_varScopeSymbolTableIndex < m_symbolTableStack.size());
     1940            SymbolTableStackEntry& varScope = m_symbolTableStack[*m_varScopeSymbolTableIndex];
     1941            SymbolTable* varSymbolTable = varScope.m_symbolTable.get();
     1942            RELEASE_ASSERT(varSymbolTable->scopeType() == SymbolTable::ScopeType::VarScope);
     1943            SymbolTableEntry entry = varSymbolTable->get(name.impl());
     1944            RELEASE_ASSERT(!entry.isNull());
     1945            bool isLexicallyScoped = false;
     1946            emitPutToScope(varScope.m_scope, variableForLocalEntry(name, entry, varScope.m_symbolTableConstantIndex, isLexicallyScoped), temp.get(), DoNotThrowIfNotFound, Initialization);
     1947        }
    19341948    }
    19351949}
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

    r198332 r198989  
    864864        Vector<SymbolTableStackEntry> m_symbolTableStack;
    865865        Vector<std::pair<VariableEnvironment, TDZCheckOptimization>> m_TDZStack;
     866        Optional<size_t> m_varScopeSymbolTableIndex;
    866867        void pushTDZVariables(VariableEnvironment, TDZCheckOptimization);
    867868
  • trunk/Source/JavaScriptCore/parser/Nodes.cpp

    r198364 r198989  
    9494}
    9595
    96 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)
     96ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, const SourceCode& source, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
    9797    : StatementNode(endLocation)
    9898    , ParserArenaRoot(parserArena)
     
    104104    , m_innerArrowFunctionCodeFeatures(innerArrowFunctionCodeFeatures)
    105105    , m_source(source)
     106    , m_sloppyModeHoistedFunctions(WTFMove(sloppyModeHoistedFunctions))
    106107    , m_numConstants(numConstants)
    107108    , m_statements(children)
     
    117118// ------------------------------ ProgramNode -----------------------------
    118119
    119 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)
    120     : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
     120ProgramNode::ProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
     121    : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
    121122    , m_startColumn(startColumn)
    122123    , m_endColumn(endColumn)
     
    126127// ------------------------------ ModuleProgramNode -----------------------------
    127128
    128 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)
    129     : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
     129ModuleProgramNode::ModuleProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
     130    : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
    130131    , m_startColumn(startColumn)
    131132    , m_endColumn(endColumn)
     
    135136// ------------------------------ EvalNode -----------------------------
    136137
    137 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)
    138     : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
     138EvalNode::EvalNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
     139    : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
    139140    , m_endColumn(endColumn)
    140141{
     
    181182// ------------------------------ FunctionNode -----------------------------
    182183
    183 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)
    184     : ScopeNode(parserArena, startLocation, endLocation, sourceCode, children, varEnvironment, WTFMove(funcStack), lexicalVariables, features, innerArrowFunctionCodeFeatures, numConstants)
     184FunctionNode::FunctionNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters* parameters, const SourceCode& sourceCode, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
     185    : ScopeNode(parserArena, startLocation, endLocation, sourceCode, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
    185186    , m_parameters(parameters)
    186187    , m_startColumn(startColumn)
  • trunk/Source/JavaScriptCore/parser/Nodes.h

    r198332 r198989  
    3737#include "VariableEnvironment.h"
    3838#include <wtf/MathExtras.h>
     39#include <wtf/SmallPtrSet.h>
    3940
    4041namespace JSC {
     
    5152    class ScopeNode;
    5253    class ModuleAnalyzer;
     54
     55    typedef SmallPtrSet<UniquedStringImpl*> UniquedStringImplPtrSet;
    5356
    5457    enum Operator {
     
    15641567
    15651568        ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, bool inStrictContext);
    1566         ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, const SourceCode&, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
     1569        ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, const SourceCode&, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
    15671570
    15681571        using ParserArenaRoot::operator new;
     
    16021605        bool captures(UniquedStringImpl* uid) { return m_varDeclarations.captures(uid); }
    16031606        bool captures(const Identifier& ident) { return captures(ident.impl()); }
     1607        bool hasSloppyModeHoistedFunction(UniquedStringImpl* uid) const { return m_sloppyModeHoistedFunctions.contains(uid); }
    16041608
    16051609        VariableEnvironment& varDeclarations() { return m_varDeclarations; }
     
    16281632        SourceCode m_source;
    16291633        VariableEnvironment m_varDeclarations;
     1634        UniquedStringImplPtrSet m_sloppyModeHoistedFunctions;
    16301635        int m_numConstants;
    16311636        SourceElements* m_statements;
     
    16341639    class ProgramNode : public ScopeNode {
    16351640    public:
    1636         ProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
     1641        ProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
    16371642
    16381643        unsigned startColumn() const { return m_startColumn; }
     
    16491654    class EvalNode : public ScopeNode {
    16501655    public:
    1651         EvalNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
     1656        EvalNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
    16521657
    16531658        ALWAYS_INLINE unsigned startColumn() const { return 0; }
     
    16641669    class ModuleProgramNode : public ScopeNode {
    16651670    public:
    1666         ModuleProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
     1671        ModuleProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
    16671672
    16681673        unsigned startColumn() const { return m_startColumn; }
     
    19051910    class FunctionNode final : public ScopeNode {
    19061911    public:
    1907         FunctionNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
     1912        FunctionNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants);
    19081913
    19091914        FunctionParameters* parameters() const { return m_parameters; }
  • trunk/Source/JavaScriptCore/parser/Parser.cpp

    r198980 r198989  
    204204    , m_defaultConstructorKind(defaultConstructorKind)
    205205    , m_thisTDZMode(thisTDZMode)
     206    , m_immediateParentAllowsFunctionDeclarationInStatement(false)
    206207{
    207208    m_lexer = std::make_unique<LexerType>(vm, builtinMode);
     
    305306
    306307    IdentifierSet capturedVariables;
     308    UniquedStringImplPtrSet sloppyModeHoistedFunctions;
    307309    bool modifiedParameter = false;
    308310    bool modifiedArguments = false;
    309     scope->getCapturedVars(capturedVariables, modifiedParameter, modifiedArguments);
     311    scope->getSloppyModeHoistedFunctions(sloppyModeHoistedFunctions);
     312    scope->getCapturedVars(capturedVariables,  modifiedParameter, modifiedArguments);
    310313
    311314    VariableEnvironment& varDeclarations = scope->declaredVariables();
     
    342345    }
    343346#endif // NDEBUG
    344     didFinishParsing(sourceElements, scope->takeFunctionDeclarations(), varDeclarations, features, context.numConstants());
     347    didFinishParsing(sourceElements, scope->takeFunctionDeclarations(), varDeclarations, WTFMove(sloppyModeHoistedFunctions), features, context.numConstants());
    345348
    346349    return parseError;
     
    349352template <typename LexerType>
    350353void Parser<LexerType>::didFinishParsing(SourceElements* sourceElements, DeclarationStacks::FunctionStack&& funcStack,
    351     VariableEnvironment& varDeclarations, CodeFeatures features, int numConstants)
     354    VariableEnvironment& varDeclarations, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, CodeFeatures features, int numConstants)
    352355{
    353356    m_sourceElements = sourceElements;
     
    355358    m_varDeclarations.swap(varDeclarations);
    356359    m_features = features;
     360    m_sloppyModeHoistedFunctions = WTFMove(sloppyModeHoistedFunctions);
    357361    m_numConstants = numConstants;
    358362}
     
    563567        if (shouldParseVariableDeclaration)
    564568            result = parseVariableDeclaration(context, DeclarationType::LetDeclaration);
    565         else
    566             result = parseExpressionOrLabelStatement(context); // Treat this as an IDENT. This is how ::parseStatement() handles IDENT.
     569        else {
     570            bool allowFunctionDeclarationAsStatement = true;
     571            result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement);
     572        }
    567573
    568574        break;
     
    576582        result = parseFunctionDeclaration(context);
    577583        break;
     584    case IDENT:
     585    case YIELD: {
     586        // This is a convenient place to notice labeled statements
     587        // (even though we also parse them as normal statements)
     588        // because we allow the following type of code in sloppy mode:
     589        // ``` function foo() { label: function bar() { } } ```
     590        bool allowFunctionDeclarationAsStatement = true;
     591        result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement);
     592        break;
     593    }
    578594    default:
    579595        m_statementDepth--; // parseStatement() increments the depth.
     
    16041620    TreeStatement result = 0;
    16051621    bool shouldSetEndOffset = true;
     1622    bool parentAllowsFunctionDeclarationAsStatement = m_immediateParentAllowsFunctionDeclarationInStatement;
     1623    m_immediateParentAllowsFunctionDeclarationInStatement = false;
    16061624
    16071625    switch (m_token.m_type) {
     
    16131631        result = parseVariableDeclaration(context, DeclarationType::VarDeclaration);
    16141632        break;
    1615     case FUNCTION:
    1616         if (!strictMode())
    1617             result = parseFunctionDeclaration(context);
    1618         else
     1633    case FUNCTION: {
     1634        if (!strictMode()) {
     1635            failIfFalse(parentAllowsFunctionDeclarationAsStatement, "Function declarations are only allowed inside block statements or at the top level of a program");
     1636            if (currentScope()->isFunction()) {
     1637                // Any function declaration that isn't in a block is a syntax error unless it's
     1638                // in an if/else statement. If it's in an if/else statement, we will magically
     1639                // treat it as if the if/else statement is inside a block statement.
     1640                // to the very top like a "var". For example:
     1641                // function a() {
     1642                //     if (cond) function foo() { }
     1643                // }
     1644                // will be rewritten as:
     1645                // function a() {
     1646                //     if (cond) { function foo() { } }
     1647                // }
     1648                AutoPopScopeRef blockScope(this, pushScope());
     1649                blockScope->setIsLexicalScope();
     1650                blockScope->preventVarDeclarations();
     1651                JSTokenLocation location(tokenLocation());
     1652                int start = tokenLine();
     1653
     1654                TreeStatement function = parseFunctionDeclaration(context);
     1655                propagateError();
     1656                failIfFalse(function, "Expected valid function statement after 'function' keyword");
     1657                TreeSourceElements sourceElements = context.createSourceElements();
     1658                context.appendStatement(sourceElements, function);
     1659                result = context.createBlockStatement(location, sourceElements, start, m_lastTokenEndPosition.line, currentScope()->finalizeLexicalEnvironment(), currentScope()->takeFunctionDeclarations());
     1660                popScope(blockScope, TreeBuilder::NeedsFreeVariableInfo);
     1661            } else {
     1662                // We only implement annex B.3.3 if we're in function mode. Otherwise, we fall back
     1663                // to hoisting behavior.
     1664                // FIXME: https://bugs.webkit.org/show_bug.cgi?id=155813
     1665                DepthManager statementDepth(&m_statementDepth);
     1666                m_statementDepth = 1;
     1667                result = parseFunctionDeclaration(context);
     1668            }
     1669        } else
    16191670            failWithMessage("Function declarations are only allowed inside blocks or switch statements in strict mode");
    16201671        break;
     1672    }
    16211673    case SEMICOLON: {
    16221674        JSTokenLocation location(tokenLocation());
     
    16681720        return 0;
    16691721    case IDENT:
    1670     case YIELD:
    1671         result = parseExpressionOrLabelStatement(context);
     1722    case YIELD: {
     1723        bool allowFunctionDeclarationAsStatement = false;
     1724        result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement);
    16721725        break;
     1726    }
    16731727    case STRING:
    16741728        directive = m_token.m_data.ident;
     
    23732427
    23742428template <typename LexerType>
    2375 template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrLabelStatement(TreeBuilder& context)
     2429template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrLabelStatement(TreeBuilder& context, bool allowFunctionDeclarationAsStatement)
    23762430{
    23772431   
     
    24242478            pushLabel(labels[i].m_ident, isLoop);
    24252479    }
     2480    m_immediateParentAllowsFunctionDeclarationInStatement = allowFunctionDeclarationAsStatement;
    24262481    TreeStatement statement = parseStatement(context, unused);
    24272482    if (!m_syntaxAlreadyValidated) {
     
    24762531
    24772532    const Identifier* unused = 0;
     2533    m_immediateParentAllowsFunctionDeclarationInStatement = true;
    24782534    TreeStatement trueBlock = parseStatement(context, unused);
    24792535    failIfFalse(trueBlock, "Expected a statement as the body of an if block");
     
    24922548        if (!match(IF)) {
    24932549            const Identifier* unused = 0;
     2550            m_immediateParentAllowsFunctionDeclarationInStatement = true;
    24942551            TreeStatement block = parseStatement(context, unused);
    24952552            failIfFalse(block, "Expected a statement as the body of an else block");
     
    25082565        handleProductionOrFail(CLOSEPAREN, ")", "end", "'if' condition");
    25092566        const Identifier* unused = 0;
     2567        m_immediateParentAllowsFunctionDeclarationInStatement = true;
    25102568        TreeStatement innerTrueBlock = parseStatement(context, unused);
    25112569        failIfFalse(innerTrueBlock, "Expected a statement as the body of an if block");
  • trunk/Source/JavaScriptCore/parser/Parser.h

    r198980 r198989  
    4141#include <wtf/Noncopyable.h>
    4242#include <wtf/RefPtr.h>
    43 #include <wtf/SmallPtrSet.h>
    4443
    4544namespace JSC {
     
    5251class ProgramNode;
    5352class SourceCode;
    54 
    55 typedef SmallPtrSet<UniquedStringImpl*> UniquedStringImplPtrSet;
    5653
    5754// Macros to make the more common TreeBuilder types a little less verbose
     
    370367    }
    371368
    372     DeclarationResultMask declareFunction(const Identifier* ident, bool declareAsVar)
     369    DeclarationResultMask declareFunction(const Identifier* ident, bool declareAsVar, bool isSloppyModeHoistingCandidate)
    373370    {
    374371        ASSERT(m_allowsVarDeclarations || m_allowsLexicalDeclarations);
     
    379376        m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
    380377        auto addResult = declareAsVar ? m_declaredVariables.add(ident->impl()) : m_lexicalVariables.add(ident->impl());
    381         addResult.iterator->value.setIsFunction();
     378        if (isSloppyModeHoistingCandidate)
     379            addResult.iterator->value.setIsSloppyModeHoistingCandidate();
    382380        if (declareAsVar) {
    383381            addResult.iterator->value.setIsVar();
     
    387385            addResult.iterator->value.setIsLet();
    388386            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.");
    389             if (!addResult.isNewEntry)
    390                 result |= DeclarationResult::InvalidDuplicateDeclaration;
    391         }
     387            if (!addResult.isNewEntry) {
     388                if (!isSloppyModeHoistingCandidate || !addResult.iterator->value.isFunction())
     389                    result |= DeclarationResult::InvalidDuplicateDeclaration;
     390            }
     391        }
     392
     393        addResult.iterator->value.setIsFunction();
     394
    392395        return result;
     396    }
     397
     398    void addSloppyModeHoistableFunctionCandidate(const Identifier* ident)
     399    {
     400        ASSERT(m_allowsVarDeclarations);
     401        m_sloppyModeHoistableFunctionCandidates.add(ident->impl());
    393402    }
    394403
     
    479488        auto addResult = m_declaredVariables.add(ident->impl());
    480489        addResult.iterator->value.clearIsVar();
     490        addResult.iterator->value.setIsParameter();
    481491        bool isValidStrictMode = addResult.isNewEntry && m_vm->propertyNames->eval != *ident && !isArgumentsIdent;
    482492        m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
     
    604614    }
    605615   
     616    void getSloppyModeHoistedFunctions(UniquedStringImplPtrSet& sloppyModeHoistedFunctions)
     617    {
     618        for (UniquedStringImpl* function : m_sloppyModeHoistableFunctionCandidates) {
     619            // ES6 Annex B.3.3. The only time we can't hoist a function is if a syntax error would
     620            // be caused by declaring a var with that function's name or if we have a parameter with
     621            // that function's name. Note that we would only cause a syntax error if we had a let/const/class
     622            // variable with the same name.
     623            if (!m_lexicalVariables.contains(function)) {
     624                auto iter = m_declaredVariables.find(function);
     625                bool isParameter = iter != m_declaredVariables.end() && iter->value.isParameter();
     626                if (!isParameter) {
     627                    auto addResult = m_declaredVariables.add(function);
     628                    addResult.iterator->value.setIsVar();
     629                    sloppyModeHoistedFunctions.add(function);
     630                }
     631            }
     632        }
     633    }
     634
    606635    void getCapturedVars(IdentifierSet& capturedVariables, bool& modifiedParameter, bool& modifiedArguments)
    607636    {
     
    743772    VariableEnvironment m_lexicalVariables;
    744773    Vector<UniquedStringImplPtrSet, 6> m_usedVariables;
     774    UniquedStringImplPtrSet m_sloppyModeHoistableFunctionCandidates;
    745775    IdentifierSet m_closedVariableCandidates;
    746776    UniquedStringImplPtrSet m_writtenVariables;
     
    769799        ASSERT(hasContainingScope());
    770800        return ScopeRef(m_scopeStack, m_index - 1);
     801    }
     802
     803    bool operator==(const ScopeRef& other)
     804    {
     805        ASSERT(other.m_scopeStack == m_scopeStack);
     806        return m_index == other.m_index;
     807    }
     808
     809    bool operator!=(const ScopeRef& other)
     810    {
     811        return !(*this == other);
    771812    }
    772813
     
    10771118    std::pair<DeclarationResultMask, ScopeRef> declareFunction(const Identifier* ident)
    10781119    {
    1079         if (m_statementDepth == 1 || !strictMode()) {
     1120        if (m_statementDepth == 1 || (!strictMode() && !currentScope()->isFunction())) {
    10801121            // Functions declared at the top-most scope (both in sloppy and strict mode) are declared as vars
    10811122            // for backwards compatibility. This allows us to declare functions with the same name more than once.
    10821123            // In sloppy mode, we always declare functions as vars.
    10831124            bool declareAsVar = true;
     1125            bool isSloppyModeHoistingCandidate = false;
    10841126            ScopeRef variableScope = currentVariableScope();
    1085             return std::make_pair(variableScope->declareFunction(ident, declareAsVar), variableScope);
     1127            return std::make_pair(variableScope->declareFunction(ident, declareAsVar, isSloppyModeHoistingCandidate), variableScope);
     1128        }
     1129
     1130        if (!strictMode()) {
     1131            ASSERT(currentScope()->isFunction());
     1132
     1133            // Functions declared inside a function inside a nested block scope in sloppy mode are subject to this
     1134            // crazy rule defined inside Annex B.3.3 in the ES6 spec. It basically states that we will create
     1135            // the function as a local block scoped variable, but when we evaluate the block that the function is
     1136            // contained in, we will assign the function to a "var" variable only if declaring such a "var" wouldn't
     1137            // be a syntax error and if there isn't a parameter with the same name. (It would only be a syntax error if
     1138            // there are is a let/class/const with the same name). Note that this mean we only do the "var" hoisting
     1139            // binding if the block evaluates. For example, this means we wont won't perform the binding if it's inside
     1140            // the untaken branch of an if statement.
     1141            bool declareAsVar = false;
     1142            bool isSloppyModeHoistingCandidate = true;
     1143            ScopeRef lexicalVariableScope = currentLexicalDeclarationScope();
     1144            ScopeRef varScope = currentVariableScope();
     1145            varScope->addSloppyModeHoistableFunctionCandidate(ident);
     1146            ASSERT(varScope != lexicalVariableScope);
     1147            return std::make_pair(lexicalVariableScope->declareFunction(ident, declareAsVar, isSloppyModeHoistingCandidate), lexicalVariableScope);
    10861148        }
    10871149
    10881150        bool declareAsVar = false;
     1151        bool isSloppyModeHoistingCandidate = false;
    10891152        ScopeRef lexicalVariableScope = currentLexicalDeclarationScope();
    1090         return std::make_pair(lexicalVariableScope->declareFunction(ident, declareAsVar), lexicalVariableScope);
     1153        return std::make_pair(lexicalVariableScope->declareFunction(ident, declareAsVar, isSloppyModeHoistingCandidate), lexicalVariableScope);
    10911154    }
    10921155
     
    11351198    String parseInner(const Identifier&, SourceParseMode);
    11361199
    1137     void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&&, VariableEnvironment&, CodeFeatures, int);
     1200    void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, CodeFeatures, int);
    11381201
    11391202    // Used to determine type of error to report.
     
    13501413    template <class TreeBuilder> TreeStatement parseDebuggerStatement(TreeBuilder&);
    13511414    template <class TreeBuilder> TreeStatement parseExpressionStatement(TreeBuilder&);
    1352     template <class TreeBuilder> TreeStatement parseExpressionOrLabelStatement(TreeBuilder&);
     1415    template <class TreeBuilder> TreeStatement parseExpressionOrLabelStatement(TreeBuilder&, bool allowFunctionDeclarationAsStatement);
    13531416    template <class TreeBuilder> TreeStatement parseIfStatement(TreeBuilder&);
    13541417    template <class TreeBuilder> TreeStatement parseBlockStatement(TreeBuilder&);
     
    15451608    VariableEnvironment m_varDeclarations;
    15461609    DeclarationStacks::FunctionStack m_funcDeclarations;
     1610    UniquedStringImplPtrSet m_sloppyModeHoistedFunctions;
    15471611    CodeFeatures m_features;
    15481612    int m_numConstants;
    15491613    ExpressionErrorClassifier* m_expressionErrorClassifier;
    15501614    bool m_isEvalContext;
     1615    bool m_immediateParentAllowsFunctionDeclarationInStatement;
    15511616   
    15521617    struct DepthManager {
     
    16181683                                    WTFMove(m_funcDeclarations),
    16191684                                    currentScope()->finalizeLexicalEnvironment(),
     1685                                    WTFMove(m_sloppyModeHoistedFunctions),
    16201686                                    m_parameters,
    16211687                                    *m_source,
  • trunk/Source/JavaScriptCore/parser/VariableEnvironment.h

    r198076 r198989  
    4242    ALWAYS_INLINE bool isImportedNamespace() const { return m_bits & IsImportedNamespace; }
    4343    ALWAYS_INLINE bool isFunction() const { return m_bits & IsFunction; }
     44    ALWAYS_INLINE bool isParameter() const { return m_bits & IsParameter; }
     45    ALWAYS_INLINE bool isSloppyModeHoistingCandidate() const { return m_bits & IsSloppyModeHoistingCandidate; }
    4446
    4547    ALWAYS_INLINE void setIsCaptured() { m_bits |= IsCaptured; }
     
    5153    ALWAYS_INLINE void setIsImportedNamespace() { m_bits |= IsImportedNamespace; }
    5254    ALWAYS_INLINE void setIsFunction() { m_bits |= IsFunction; }
     55    ALWAYS_INLINE void setIsParameter() { m_bits |= IsParameter; }
     56    ALWAYS_INLINE void setIsSloppyModeHoistingCandidate() { m_bits |= IsSloppyModeHoistingCandidate; }
    5357
    5458    ALWAYS_INLINE void clearIsVar() { m_bits &= ~IsVar; }
    5559
    5660private:
    57     enum Traits : uint8_t {
     61    enum Traits : uint16_t {
    5862        IsCaptured = 1 << 0,
    5963        IsConst = 1 << 1,
     
    6367        IsImported = 1 << 5,
    6468        IsImportedNamespace = 1 << 6,
    65         IsFunction = 1 << 7
     69        IsFunction = 1 << 7,
     70        IsParameter = 1 << 8,
     71        IsSloppyModeHoistingCandidate = 1 << 9
    6672    };
    67     uint8_t m_bits { 0 };
     73    uint16_t m_bits { 0 };
    6874};
    6975
  • trunk/Source/JavaScriptCore/runtime/CodeCache.h

    r198980 r198989  
    3333#include "SourceCodeKey.h"
    3434#include "Strong.h"
    35 #include "VariableEnvironment.h"
    3635#include <wtf/CurrentTime.h>
    3736#include <wtf/Forward.h>
     
    5857class SourceCode;
    5958class SourceProvider;
     59class VariableEnvironment;
    6060
    6161struct SourceCodeValue {
  • trunk/Source/JavaScriptCore/runtime/JSScope.cpp

    r198228 r198989  
    3333#include "JSWithScope.h"
    3434#include "JSCInlines.h"
     35#include "VariableEnvironment.h"
    3536
    3637namespace JSC {
  • trunk/Source/JavaScriptCore/runtime/JSScope.h

    r198228 r198989  
    2929#include "GetPutInfo.h"
    3030#include "JSObject.h"
    31 #include "VariableEnvironment.h"
    3231
    3332namespace JSC {
     
    3534class ScopeChainIterator;
    3635class WatchpointSet;
     36class VariableEnvironment;
    3737
    3838class JSScope : public JSNonFinalObject {
  • trunk/Source/JavaScriptCore/tests/es6.yaml

    r198985 r198989  
    882882  cmd: runES6 :normal
    883883- path: es6/non-strict_function_semantics_hoisted_block-level_function_declaration.js
    884   cmd: runES6 :fail
     884  cmd: runES6 :normal
    885885- path: es6/Promise_is_subclassable_Promise.all.js
    886886  cmd: runES6 :fail
Note: See TracChangeset for help on using the changeset viewer.