Ignore:
Timestamp:
Apr 18, 2017, 12:35:50 PM (8 years ago)
Author:
[email protected]
Message:

[ES6]. Implement Annex B.3.3 function hoisting rules for eval
https://bugs.webkit.org/show_bug.cgi?id=163208

Reviewed by Saam Barati.

JSTests:

  • stress/eval-func-decl-block-scoping-reassign.js: Added.

(assert):
(throw.new.Error.f):
(throw.new.Error):

  • stress/eval-func-decl-block-with-remove.js: Added.

(assert):
(foo.boo):
(foo):

  • stress/eval-func-decl-block-with-var-and-remove.js: Added.

(assert):
(assertThrow):
(foo):
(boo):
(joo):
(koo):

  • stress/eval-func-decl-block-with-var-sinthesize.js: Added.

(assert):
(assertThrow):
(foo):
(boo):
(hoo):
(joo):
(koo):

  • stress/eval-func-decl-in-block-scope-and-bind-to-top-eval-scope.js: Added.
  • stress/eval-func-decl-in-eval-within-block-with-let.js: Added.

(assert):
(assertThrow):
(foo):
(boo):
(goo):

  • stress/eval-func-decl-in-eval-within-with-scope.js: Added.

(assert):
(assertThrow):
(foo):
(boo):
(boo.let.val2):
(boo.let.val3):

  • stress/eval-func-decl-in-frozen-global.js: Added.

(assert):
(assertThrow):
(throw.new.Error):
(Object.freeze):

  • stress/eval-func-decl-in-global-of-eval.js: Added.

(assert):
(assertThrow):
(bar):
(baz):
(foobar):

  • stress/eval-func-decl-in-global.js: Added.

(assert):
(assertThrow):

  • stress/eval-func-decl-in-if.js: Added.

(assert):

  • stress/eval-func-decl-within-eval-with-reassign-to-var.js: Added.

(assert):
(assertThrow):
(foo):
(boo):
(foobar):
(hoo):
(joo):
(koo):
(loo):

  • stress/eval-func-decl-within-eval-without-reassign-to-let.js: Added.

(assert):
(assertThrow):
(foo):
(boo):
(goo):

  • stress/variable-under-tdz-eval-tricky.js:

(assert):

  • test262.yaml:

Source/JavaScriptCore:

Current patch implements Annex B.3.3 that is related to
hoisting of function declaration in eval.
https://tc39.github.io/ecma262/#sec-web-compat-evaldeclarationinstantiation
Function declaration in eval should create variable with
function name in function scope where eval is invoked
or bind to variable if it declared outside of the eval.
If variable is created it can be removed by 'delete a;' command.
If eval is invoke in block scope that contains let/const
variable with the same name as function declaration
we do not bind. This patch leads to the following behavior:

function foo() {

{

print(boo); undefined
eval('{ function boo() {}}');
print(boo);
function boo() {}

}
print(boo); function boo() {}

}

function foobar() {

{

let boo = 10;
print(boo); 10;
eval('{ function boo() {}}');
print(boo);
10;

}
print(boo) 10

}

function bar() {

{

var boo = 10;
print(boo); 10
eval('{ function boo() {} }');
print(boo);
function boo() {}

}
print(boo); function boo() {}

}

function bas() {

{

let boo = 10;
eval(' { function boo() {} } ');
print(boo); 10

}
print(boo); Reference Error

}

Current implementation relies on already implemented
'hoist function in sloppy mode' feature, with small changes.
In short it works in following way: during hoisting of function
with name S in eval, we are looking for first scope that
contains space for variable with name S and if this scope
has var type we bind function there

To implement this feature was added bytecode ops:
op_resolve_scope_for_hoisting_func_decl_in_eval - get variable scope
or return undefined if variable can't be binded there.

There is a corner case, hoist function in eval within catch block,
that is not covered by this patch, and will be fixed in
https://bugs.webkit.org/show_bug.cgi?id=168184

  • bytecode/BytecodeDumper.cpp:

(JSC::BytecodeDumper<Block>::dumpBytecode):

  • bytecode/BytecodeList.json:
  • bytecode/BytecodeUseDef.h:

(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::finalizeLLIntInlineCaches):

  • bytecode/EvalCodeBlock.h:

(JSC::EvalCodeBlock::functionHoistingCandidate):
(JSC::EvalCodeBlock::numFunctionHoistingCandidates):

  • bytecode/UnlinkedEvalCodeBlock.h:
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::hoistSloppyModeFunctionIfNecessary):
(JSC::BytecodeGenerator::emitResolveScopeForHoistingFuncDeclInEval):

  • bytecompiler/BytecodeGenerator.h:
  • dfg/DFGAbstractInterpreterInlines.h:

(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGCapabilities.cpp:

(JSC::DFG::capabilityLevel):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):

  • dfg/DFGNode.h:

(JSC::DFG::Node::hasIdentifier):

  • dfg/DFGNodeType.h:
  • dfg/DFGOperations.cpp:
  • dfg/DFGOperations.h:
  • dfg/DFGPredictionPropagationPhase.cpp:
  • dfg/DFGSafeToExecute.h:

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileResolveScopeForHoistingFuncDeclInEval):

  • dfg/DFGSpeculativeJIT.h:

(JSC::DFG::SpeculativeJIT::callOperation):

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileResolveScopeForHoistingFuncDeclInEval):

  • interpreter/Interpreter.cpp:

(JSC::Interpreter::execute):

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):

  • jit/JIT.h:
  • jit/JITOperations.h:
  • jit/JITPropertyAccess.cpp:

(JSC::JIT::emit_op_resolve_scope_for_hoisting_func_decl_in_eval):

  • jit/JITPropertyAccess32_64.cpp:

(JSC::JIT::emit_op_resolve_scope_for_hoisting_func_decl_in_eval):

  • llint/LowLevelInterpreter.asm:
  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseFunctionDeclarationStatement):

  • parser/Parser.h:

(JSC::Scope::getSloppyModeHoistedFunctions):
(JSC::Parser::declareFunction):

  • runtime/CommonSlowPaths.cpp:

(JSC::SLOW_PATH_DECL):

  • runtime/CommonSlowPaths.h:
  • runtime/EvalExecutable.h:

(JSC::EvalExecutable::numFunctionHoistingCandidates):
(JSC::EvalExecutable::numTopLevelFunctionDecls):
(JSC::EvalExecutable::numberOfFunctionDecls): Deleted.

  • runtime/JSScope.cpp:

(JSC::JSScope::resolve):
(JSC::JSScope::resolveScopeForHoistingFuncDeclInEval):

  • runtime/JSScope.h:

LayoutTests:

  • inspector/runtime/evaluate-CommandLineAPI-expected.txt:
  • inspector/runtime/evaluate-CommandLineAPI.html:
  • js/parser-syntax-check-expected.txt:
  • js/script-tests/parser-syntax-check.js:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/interpreter/Interpreter.cpp

    r215072 r215476  
    10921092
    10931093    unsigned numVariables = eval->numVariables();
    1094     int numFunctions = eval->numberOfFunctionDecls();
     1094    unsigned numTopLevelFunctionDecls = eval->numTopLevelFunctionDecls();
     1095    unsigned numFunctionHoistingCandidates = eval->numFunctionHoistingCandidates();
    10951096
    10961097    JSScope* variableObject;
    1097     if ((numVariables || numFunctions) && eval->isStrictMode()) {
     1098    if ((numVariables || numTopLevelFunctionDecls) && eval->isStrictMode()) {
    10981099        scope = StrictEvalActivation::create(callFrame, scope);
    10991100        variableObject = scope;
     
    11261127
    11271128    // We can't declare a "var"/"function" that overwrites a global "let"/"const"/"class" in a sloppy-mode eval.
    1128     if (variableObject->isGlobalObject() && !eval->isStrictMode() && (numVariables || numFunctions)) {
     1129    if (variableObject->isGlobalObject() && !eval->isStrictMode() && (numVariables || numTopLevelFunctionDecls)) {
    11291130        JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalObject*>(variableObject)->globalLexicalEnvironment();
    11301131        for (unsigned i = 0; i < numVariables; ++i) {
     
    11361137        }
    11371138
    1138         for (int i = 0; i < numFunctions; ++i) {
     1139        for (unsigned i = 0; i < numTopLevelFunctionDecls; ++i) {
    11391140            FunctionExecutable* function = codeBlock->functionDecl(i);
    11401141            PropertySlot slot(globalLexicalEnvironment, PropertySlot::InternalMethodType::VMInquiry);
     
    11481149        variableObject->flattenDictionaryObject(vm);
    11491150
    1150     if (numVariables || numFunctions) {
     1151    if (numVariables || numTopLevelFunctionDecls || numFunctionHoistingCandidates) {
    11511152        BatchedTransitionOptimizer optimizer(vm, variableObject);
    11521153        if (variableObject->next() && !eval->isStrictMode())
     
    11651166            }
    11661167        }
    1167 
    1168         for (int i = 0; i < numFunctions; ++i) {
    1169             FunctionExecutable* function = codeBlock->functionDecl(i);
    1170             PutPropertySlot slot(variableObject);
    1171             variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot);
    1172             RETURN_IF_EXCEPTION(throwScope, checkedReturn(throwScope.exception()));
     1168       
     1169        if (eval->isStrictMode()) {
     1170            for (unsigned i = 0; i < numTopLevelFunctionDecls; ++i) {
     1171                FunctionExecutable* function = codeBlock->functionDecl(i);
     1172                PutPropertySlot slot(variableObject);
     1173                variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot);
     1174            }
     1175        } else {
     1176            for (unsigned i = 0; i < numTopLevelFunctionDecls; ++i) {
     1177                FunctionExecutable* function = codeBlock->functionDecl(i);
     1178                JSValue resolvedScope = JSScope::resolveScopeForHoistingFuncDeclInEval(callFrame, scope, function->name());
     1179                if (resolvedScope.isUndefined())
     1180                    return checkedReturn(throwSyntaxError(callFrame, throwScope, makeString("Can't create duplicate variable in eval: '", String(function->name().impl()), "'")));
     1181                PutPropertySlot slot(variableObject);
     1182                variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot);
     1183                RETURN_IF_EXCEPTION(throwScope, checkedReturn(throwScope.exception()));
     1184            }
     1185
     1186            for (unsigned i = 0; i < numFunctionHoistingCandidates; ++i) {
     1187                const Identifier& ident = codeBlock->functionHoistingCandidate(i);
     1188                JSValue resolvedScope = JSScope::resolveScopeForHoistingFuncDeclInEval(callFrame, scope, ident);
     1189                if (!resolvedScope.isUndefined()) {
     1190                    if (!variableObject->hasProperty(callFrame, ident)) {
     1191                        PutPropertySlot slot(variableObject);
     1192                        variableObject->methodTable()->put(variableObject, callFrame, ident, jsUndefined(), slot);
     1193                        RETURN_IF_EXCEPTION(throwScope, checkedReturn(throwScope.exception()));
     1194                    }
     1195                }
     1196            }
    11731197        }
    11741198    }
Note: See TracChangeset for help on using the changeset viewer.