Ignore:
Timestamp:
Apr 30, 2017, 1:06:23 AM (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

    r215854 r215984  
    11001100
    11011101    unsigned numVariables = eval->numVariables();
    1102     int numFunctions = eval->numberOfFunctionDecls();
     1102    unsigned numTopLevelFunctionDecls = eval->numTopLevelFunctionDecls();
     1103    unsigned numFunctionHoistingCandidates = eval->numFunctionHoistingCandidates();
    11031104
    11041105    JSScope* variableObject;
    1105     if ((numVariables || numFunctions) && eval->isStrictMode()) {
     1106    if ((numVariables || numTopLevelFunctionDecls) && eval->isStrictMode()) {
    11061107        scope = StrictEvalActivation::create(callFrame, scope);
    11071108        variableObject = scope;
     
    11341135
    11351136    // We can't declare a "var"/"function" that overwrites a global "let"/"const"/"class" in a sloppy-mode eval.
    1136     if (variableObject->isGlobalObject() && !eval->isStrictMode() && (numVariables || numFunctions)) {
     1137    if (variableObject->isGlobalObject() && !eval->isStrictMode() && (numVariables || numTopLevelFunctionDecls)) {
    11371138        JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalObject*>(variableObject)->globalLexicalEnvironment();
    11381139        for (unsigned i = 0; i < numVariables; ++i) {
     
    11441145        }
    11451146
    1146         for (int i = 0; i < numFunctions; ++i) {
     1147        for (unsigned i = 0; i < numTopLevelFunctionDecls; ++i) {
    11471148            FunctionExecutable* function = codeBlock->functionDecl(i);
    11481149            PropertySlot slot(globalLexicalEnvironment, PropertySlot::InternalMethodType::VMInquiry);
     
    11561157        variableObject->flattenDictionaryObject(vm);
    11571158
    1158     if (numVariables || numFunctions) {
     1159    if (numVariables || numTopLevelFunctionDecls || numFunctionHoistingCandidates) {
    11591160        BatchedTransitionOptimizer optimizer(vm, variableObject);
    11601161        if (variableObject->next() && !eval->isStrictMode())
     
    11731174            }
    11741175        }
    1175 
    1176         for (int i = 0; i < numFunctions; ++i) {
    1177             FunctionExecutable* function = codeBlock->functionDecl(i);
    1178             PutPropertySlot slot(variableObject);
    1179             variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot);
    1180             RETURN_IF_EXCEPTION(throwScope, checkedReturn(throwScope.exception()));
     1176       
     1177        if (eval->isStrictMode()) {
     1178            for (unsigned i = 0; i < numTopLevelFunctionDecls; ++i) {
     1179                FunctionExecutable* function = codeBlock->functionDecl(i);
     1180                PutPropertySlot slot(variableObject);
     1181                variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot);
     1182            }
     1183        } else {
     1184            for (unsigned i = 0; i < numTopLevelFunctionDecls; ++i) {
     1185                FunctionExecutable* function = codeBlock->functionDecl(i);
     1186                JSValue resolvedScope = JSScope::resolveScopeForHoistingFuncDeclInEval(callFrame, scope, function->name());
     1187                if (resolvedScope.isUndefined())
     1188                    return checkedReturn(throwSyntaxError(callFrame, throwScope, makeString("Can't create duplicate variable in eval: '", String(function->name().impl()), "'")));
     1189                PutPropertySlot slot(variableObject);
     1190                variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot);
     1191                RETURN_IF_EXCEPTION(throwScope, checkedReturn(throwScope.exception()));
     1192            }
     1193
     1194            for (unsigned i = 0; i < numFunctionHoistingCandidates; ++i) {
     1195                const Identifier& ident = codeBlock->functionHoistingCandidate(i);
     1196                JSValue resolvedScope = JSScope::resolveScopeForHoistingFuncDeclInEval(callFrame, scope, ident);
     1197                if (!resolvedScope.isUndefined()) {
     1198                    if (!variableObject->hasProperty(callFrame, ident)) {
     1199                        PutPropertySlot slot(variableObject);
     1200                        variableObject->methodTable()->put(variableObject, callFrame, ident, jsUndefined(), slot);
     1201                        RETURN_IF_EXCEPTION(throwScope, checkedReturn(throwScope.exception()));
     1202                    }
     1203                }
     1204            }
    11811205        }
    11821206    }
Note: See TracChangeset for help on using the changeset viewer.