haikuwebkit/LayoutTests/js/let-syntax-expected.txt

452 lines
33 KiB
Plaintext
Raw Permalink Normal View History

[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
Tests for ES6 "let"
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS Does not have syntax error: 'let x = 20; if (truth()) { let x = 30; }'
PASS Does not have syntax error: ''use strict'; let x = 20; if (truth()) { let x = 30; }'
PASS Does not have syntax error: 'let {x} = {x:20}; if (truth()) { let {x} = {x : 20}; }'
PASS Does not have syntax error: ''use strict'; let {x} = {x:20}; if (truth()) { let {x} = {x : 20}; }'
PASS Does not have syntax error: 'let {x} = {x:20}; if (truth()) { let {y: x} = {y : 20}; }'
PASS Does not have syntax error: ''use strict'; let {x} = {x:20}; if (truth()) { let {y: x} = {y : 20}; }'
PASS Does not have syntax error: 'let {x, y: [arr]} = {x:20, y: [10]}; if (truth()) { let {y: x} = {y : 20}; }'
PASS Does not have syntax error: ''use strict'; let {x, y: [arr]} = {x:20, y: [10]}; if (truth()) { let {y: x} = {y : 20}; }'
PASS Does not have syntax error: 'let i = 40; for (let i = 1; i < 2; i++) { let i = 40; i; }'
PASS Does not have syntax error: ''use strict'; let i = 40; for (let i = 1; i < 2; i++) { let i = 40; i; }'
PASS Does not have syntax error: 'let i = 40; let obj = {}; for (let i in obj) { let i = 40; let obj = {}; i; }'
PASS Does not have syntax error: ''use strict'; let i = 40; let obj = {}; for (let i in obj) { let i = 40; let obj = {}; i; }'
PASS Does not have syntax error: 'let i = 40; let obj = []; for (let i of obj) { let i = 40; let obj = {}; i; }'
PASS Does not have syntax error: ''use strict'; let i = 40; let obj = []; for (let i of obj) { let i = 40; let obj = {}; i; }'
PASS Does not have syntax error: 'let {i} = 20; let obj = []; for (let {i} of obj) { let i = 40; let obj = {}; i; }'
PASS Does not have syntax error: ''use strict'; let {i} = 20; let obj = []; for (let {i} of obj) { let i = 40; let obj = {}; i; }'
PASS Does not have syntax error: 'let {i} = 20; let obj = []; for (let {i} in obj) { let i = 40; let obj = {}; i; }'
PASS Does not have syntax error: ''use strict'; let {i} = 20; let obj = []; for (let {i} in obj) { let i = 40; let obj = {}; i; }'
PASS Does not have syntax error: 'let {i} = 20; let obj = []; for (let {i} = {i: 0}; i < 2; i++) { let i = 40; let obj = {}; i; }'
PASS Does not have syntax error: ''use strict'; let {i} = 20; let obj = []; for (let {i} = {i: 0}; i < 2; i++) { let i = 40; let obj = {}; i; }'
PASS Does not have syntax error: 'function foo() { let foo = 20; }'
PASS Does not have syntax error: ''use strict'; function foo() { let foo = 20; }'
PASS Does not have syntax error: 'function foo(bar) { if (truth()) { let bar; } }'
PASS Does not have syntax error: ''use strict'; function foo(bar) { if (truth()) { let bar; } }'
PASS Does not have syntax error: 'function foo() { var bar; if (truth()) { let bar; } }'
PASS Does not have syntax error: ''use strict'; function foo() { var bar; if (truth()) { let bar; } }'
PASS Does not have syntax error: ';({ get let() { return 50; }, set let(x) { return 50;} });'
PASS Does not have syntax error: ''use strict'; ;({ get let() { return 50; }, set let(x) { return 50;} });'
PASS Does not have syntax error: 'function foo() { { let x; } var x; }'
PASS Does not have syntax error: ''use strict'; function foo() { { let x; } var x; }'
PASS Does not have syntax error: 'var x = (function foo() { const foo = 20; });'
PASS Does not have syntax error: ''use strict'; var x = (function foo() { const foo = 20; });'
PASS Does not have syntax error: 'var x = (function foo() { let foo = 20; });'
PASS Does not have syntax error: ''use strict'; var x = (function foo() { let foo = 20; });'
PASS Does not have syntax error: 'var x = (function foo() { class foo { } });'
PASS Does not have syntax error: ''use strict'; var x = (function foo() { class foo { } });'
PASS Does not have syntax error: ';(function foo() { const foo = 20; });'
PASS Does not have syntax error: ''use strict'; ;(function foo() { const foo = 20; });'
PASS Does not have syntax error: ';(function foo() { let foo = 20; });'
PASS Does not have syntax error: ''use strict'; ;(function foo() { let foo = 20; });'
PASS Does not have syntax error: ';(function foo() { class foo { } });'
PASS Does not have syntax error: ''use strict'; ;(function foo() { class foo { } });'
PASS Does not have syntax error: 'let x = {f: function foo() { const foo = 20; } };'
PASS Does not have syntax error: ''use strict'; let x = {f: function foo() { const foo = 20; } };'
PASS Does not have syntax error: 'let x = {f: function foo() { let foo = 20; } };'
PASS Does not have syntax error: ''use strict'; let x = {f: function foo() { let foo = 20; } };'
PASS Does not have syntax error: 'let x = {f: function foo() { class foo { } } };'
PASS Does not have syntax error: ''use strict'; let x = {f: function foo() { class foo { } } };'
PASS Does not have syntax error: 'let x = { foo() { const foo = 20; } };'
PASS Does not have syntax error: ''use strict'; let x = { foo() { const foo = 20; } };'
PASS Does not have syntax error: 'let x = { foo() { let foo = 20; } };'
PASS Does not have syntax error: ''use strict'; let x = { foo() { let foo = 20; } };'
PASS Does not have syntax error: 'let x = { foo() { class foo { } } };'
PASS Does not have syntax error: ''use strict'; let x = { foo() { class foo { } } };'
PASS Does not have syntax error: 'let x = { set foo(x) { const foo = 20; } };'
PASS Does not have syntax error: ''use strict'; let x = { set foo(x) { const foo = 20; } };'
PASS Does not have syntax error: 'let x = { set foo(x) { let foo = 20; } };'
PASS Does not have syntax error: ''use strict'; let x = { set foo(x) { let foo = 20; } };'
PASS Does not have syntax error: 'let x = { set foo(x) { class foo { } } };'
PASS Does not have syntax error: ''use strict'; let x = { set foo(x) { class foo { } } };'
PASS Does not have syntax error: 'let x = { get foo() { const foo = 20; } };'
PASS Does not have syntax error: ''use strict'; let x = { get foo() { const foo = 20; } };'
PASS Does not have syntax error: 'let x = { get foo() { let foo = 20; } };'
PASS Does not have syntax error: ''use strict'; let x = { get foo() { let foo = 20; } };'
PASS Does not have syntax error: 'let x = { get foo() { class foo { } } };'
PASS Does not have syntax error: ''use strict'; let x = { get foo() { class foo { } } };'
PASS Does not have syntax error: 'let x; with ({}) let: y = 3;'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let let;'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Cannot use 'let' as a lexical variable name in strict mode.
SyntaxError: Cannot use 'let' as a lexical variable name in strict mode.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let let;'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'const let;'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Cannot use 'let' as a lexical variable name in strict mode.
SyntaxError: Cannot use 'let' as a lexical variable name in strict mode.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; const let;'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let {let};'
SyntaxError: Cannot use abbreviated destructuring syntax for keyword 'let'.
SyntaxError: Cannot use abbreviated destructuring syntax for keyword 'let'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let {let};'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let {l: let};'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Cannot use 'let' as a lexical variable name in strict mode.
SyntaxError: Cannot use 'let' as a lexical variable name in strict mode.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let {l: let};'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let {l: {let}};'
SyntaxError: Cannot use abbreviated destructuring syntax for keyword 'let'.
SyntaxError: Cannot use abbreviated destructuring syntax for keyword 'let'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let {l: {let}};'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let {l: [let]};'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Cannot use 'let' as a lexical variable name in strict mode.
SyntaxError: Cannot use 'let' as a lexical variable name in strict mode.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let {l: [let]};'
SyntaxError: Unexpected token ';'. Expected an initializer in destructuring variable declaration.
SyntaxError: Unexpected token ';'. Expected an initializer in destructuring variable declaration.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'var {let};'
SyntaxError: Cannot use abbreviated destructuring syntax for keyword 'let'.
SyntaxError: Cannot use abbreviated destructuring syntax for keyword 'let'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; var {let};'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let x, x;'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let x, x;'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let x = 20, y, x = 40;'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let x = 20, y, x = 40;'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let x = 20, y; let x = 40;'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let x = 20, y; let x = 40;'
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let x = 20, y, {x} = {};'
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let x = 20, y, {x} = {};'
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let x = 20, y; let {x} = {};'
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let x = 20, y; let {x} = {};'
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let {x, y, z, x} = {};'
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let {x, y, z, x} = {};'
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let {x: xx, y, x: xx} = {};'
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let {x: xx, y, x: xx} = {};'
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let {x: xx, foo: [xx]} = {foo:[12]};'
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let {x: xx, foo: [xx]} = {foo:[12]};'
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let {x: xx, foo: {y: xx}} = {foo:[12]};'
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
SyntaxError: Unexpected identifier 'xx'. Cannot declare a lexical variable twice: 'xx'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let {x: xx, foo: {y: xx}} = {foo:[12]};'
SyntaxError: Unexpected token ';'. Expected a parameter pattern or a ')' in parameter list.
SyntaxError: Unexpected token ';'. Expected a parameter pattern or a ')' in parameter list.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'for (let; ; ) {}'
SyntaxError: Unexpected token ';'. Expected a parameter pattern or a ')' in parameter list.
SyntaxError: Unexpected token ';'. Expected a parameter pattern or a ')' in parameter list.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; for (let; ; ) {}'
SyntaxError: Unexpected identifier 'arr'. Expected either 'in' or 'of' in enumeration syntax.
SyntaxError: Unexpected identifier 'arr'. Expected either 'in' or 'of' in enumeration syntax.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let arr = []; for (let of arr) {}'
SyntaxError: Unexpected identifier 'arr'. Expected either 'in' or 'of' in enumeration syntax.
SyntaxError: Unexpected identifier 'arr'. Expected either 'in' or 'of' in enumeration syntax.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let arr = []; for (let of arr) {}'
[ES6] Catch parameter should accept BindingPattern https://bugs.webkit.org/show_bug.cgi?id=152385 Reviewed by Saam Barati. Source/JavaScriptCore: This patch implements destructuring in catch parameter. Catch parameter accepts binding pattern and binding identifier. It creates lexical bindings. And "yield" and "let" are specially handled as is the same to function parameters. In addition to that, we make destructuring parsing errors more descriptive. * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::emitPushCatchScope): * bytecompiler/BytecodeGenerator.h: * bytecompiler/NodesCodegen.cpp: (JSC::TryNode::emitBytecode): * parser/ASTBuilder.h: (JSC::ASTBuilder::createTryStatement): * parser/NodeConstructors.h: (JSC::TryNode::TryNode): * parser/Nodes.h: * parser/Parser.cpp: (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseBindingOrAssignmentElement): (JSC::destructuringKindToVariableKindName): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseFormalParameters): (JSC::Parser<LexerType>::parseFunctionParameters): * parser/Parser.h: (JSC::Parser::destructuringKindFromDeclarationType): * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createTryStatement): * tests/es6.yaml: * tests/es6/destructuring_in_catch_heads.js: Added. (test): * tests/stress/catch-parameter-destructuring.js: Added. (shouldBe): (shouldThrow): (prototype.call): (catch): (shouldThrow.try.throw.get error): (initialize): (array): (generator.gen): (generator): * tests/stress/catch-parameter-syntax.js: Added. (testSyntax): (testSyntaxError): * tests/stress/reserved-word-with-escape.js: (testSyntaxError.String.raw.a): (String.raw.SyntaxError.Cannot.use.the.keyword.string_appeared_here.as.a.name): * tests/stress/yield-named-variable.js: LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/mozilla/strict/12.14.1-expected.txt: * js/mozilla/strict/script-tests/12.14.1.js: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T10-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T13-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T5-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T7-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T8-expected.txt: Canonical link: https://commits.webkit.org/171405@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@195439 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-01-22 04:21:36 +00:00
SyntaxError: Cannot use the keyword 'in' as a lexical variable name.
SyntaxError: Cannot use the keyword 'in' as a lexical variable name.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let obj = {}; for (let in arr) {}'
[ES6] Catch parameter should accept BindingPattern https://bugs.webkit.org/show_bug.cgi?id=152385 Reviewed by Saam Barati. Source/JavaScriptCore: This patch implements destructuring in catch parameter. Catch parameter accepts binding pattern and binding identifier. It creates lexical bindings. And "yield" and "let" are specially handled as is the same to function parameters. In addition to that, we make destructuring parsing errors more descriptive. * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::emitPushCatchScope): * bytecompiler/BytecodeGenerator.h: * bytecompiler/NodesCodegen.cpp: (JSC::TryNode::emitBytecode): * parser/ASTBuilder.h: (JSC::ASTBuilder::createTryStatement): * parser/NodeConstructors.h: (JSC::TryNode::TryNode): * parser/Nodes.h: * parser/Parser.cpp: (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseBindingOrAssignmentElement): (JSC::destructuringKindToVariableKindName): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseFormalParameters): (JSC::Parser<LexerType>::parseFunctionParameters): * parser/Parser.h: (JSC::Parser::destructuringKindFromDeclarationType): * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createTryStatement): * tests/es6.yaml: * tests/es6/destructuring_in_catch_heads.js: Added. (test): * tests/stress/catch-parameter-destructuring.js: Added. (shouldBe): (shouldThrow): (prototype.call): (catch): (shouldThrow.try.throw.get error): (initialize): (array): (generator.gen): (generator): * tests/stress/catch-parameter-syntax.js: Added. (testSyntax): (testSyntaxError): * tests/stress/reserved-word-with-escape.js: (testSyntaxError.String.raw.a): (String.raw.SyntaxError.Cannot.use.the.keyword.string_appeared_here.as.a.name): * tests/stress/yield-named-variable.js: LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/mozilla/strict/12.14.1-expected.txt: * js/mozilla/strict/script-tests/12.14.1.js: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T10-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T13-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T5-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T7-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T8-expected.txt: Canonical link: https://commits.webkit.org/171405@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@195439 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-01-22 04:21:36 +00:00
SyntaxError: Cannot use the keyword 'in' as a lexical variable name.
SyntaxError: Cannot use the keyword 'in' as a lexical variable name.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let obj = {}; for (let in arr) {}'
SyntaxError: Cannot declare a let variable twice: 'i'.
SyntaxError: Cannot declare a let variable twice: 'i'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'for (let i = 20, j = 40, i = 10; i < 10; i++) {}'
SyntaxError: Cannot declare a let variable twice: 'i'.
SyntaxError: Cannot declare a let variable twice: 'i'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; for (let i = 20, j = 40, i = 10; i < 10; i++) {}'
SyntaxError: Unexpected identifier 'x'
SyntaxError: Unexpected identifier 'x'
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let x = 20; if (truth()) let x = 40;'
SyntaxError: Unexpected keyword 'let'
SyntaxError: Unexpected keyword 'let'
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let x = 20; if (truth()) let x = 40;'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'let baz = 20; if (truth()) { let x = 20; let x = 40;} '
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let baz = 20; if (truth()) { let x = 20; let x = 40;} '
SyntaxError: Cannot declare a let variable twice: 'bar'.
SyntaxError: Cannot declare a let variable twice: 'bar'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'function foo() { var bar; let bar; }'
SyntaxError: Cannot declare a let variable twice: 'bar'.
SyntaxError: Cannot declare a let variable twice: 'bar'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; function foo() { var bar; let bar; }'
SyntaxError: Cannot declare a let variable twice: 'bar'.
SyntaxError: Cannot declare a let variable twice: 'bar'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'function foo(bar) { let bar; }'
SyntaxError: Cannot declare a let variable twice: 'bar'.
SyntaxError: Cannot declare a let variable twice: 'bar'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; function foo(bar) { let bar; }'
SyntaxError: Cannot declare a let variable twice: 'foo'.
SyntaxError: Cannot declare a let variable twice: 'foo'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'function foo() {}; let foo;'
SyntaxError: Cannot declare a let variable twice: 'foo'.
SyntaxError: Cannot declare a let variable twice: 'foo'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; function foo() {}; let foo;'
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'bar'.
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'bar'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'function foo() {}; function bar(){} let baz, {bar} = {};'
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'bar'.
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'bar'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; function foo() {}; function bar(){} let baz, {bar} = {};'
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'bar'.
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'bar'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'function foo() {}; function bar(){} let baz, {f: {bar}} = {f:{}};'
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'bar'.
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'bar'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; function foo() {}; function bar(){} let baz, {f: {bar}} = {f:{}};'
SyntaxError: Unexpected identifier 'bar'. Cannot declare a lexical variable twice: 'bar'.
SyntaxError: Unexpected identifier 'bar'. Cannot declare a lexical variable twice: 'bar'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'function foo() {}; function bar(){} let baz, {f: [bar]} = {f:[10]};'
SyntaxError: Unexpected identifier 'bar'. Cannot declare a lexical variable twice: 'bar'.
SyntaxError: Unexpected identifier 'bar'. Cannot declare a lexical variable twice: 'bar'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; function foo() {}; function bar(){} let baz, {f: [bar]} = {f:[10]};'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'for (let let = 0; let < 10; let++) {}'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Cannot use 'let' as a lexical variable name in strict mode.
SyntaxError: Cannot use 'let' as a lexical variable name in strict mode.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; for (let let = 0; let < 10; let++) {}'
SyntaxError: Unexpected token '['. Expected either 'in' or 'of' in enumeration syntax.
SyntaxError: Unexpected token '['. Expected either 'in' or 'of' in enumeration syntax.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'for (let of []) {}'
SyntaxError: Unexpected token '['. Expected either 'in' or 'of' in enumeration syntax.
SyntaxError: Unexpected token '['. Expected either 'in' or 'of' in enumeration syntax.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; for (let of []) {}'
[ES6] Catch parameter should accept BindingPattern https://bugs.webkit.org/show_bug.cgi?id=152385 Reviewed by Saam Barati. Source/JavaScriptCore: This patch implements destructuring in catch parameter. Catch parameter accepts binding pattern and binding identifier. It creates lexical bindings. And "yield" and "let" are specially handled as is the same to function parameters. In addition to that, we make destructuring parsing errors more descriptive. * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::emitPushCatchScope): * bytecompiler/BytecodeGenerator.h: * bytecompiler/NodesCodegen.cpp: (JSC::TryNode::emitBytecode): * parser/ASTBuilder.h: (JSC::ASTBuilder::createTryStatement): * parser/NodeConstructors.h: (JSC::TryNode::TryNode): * parser/Nodes.h: * parser/Parser.cpp: (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseBindingOrAssignmentElement): (JSC::destructuringKindToVariableKindName): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseFormalParameters): (JSC::Parser<LexerType>::parseFunctionParameters): * parser/Parser.h: (JSC::Parser::destructuringKindFromDeclarationType): * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createTryStatement): * tests/es6.yaml: * tests/es6/destructuring_in_catch_heads.js: Added. (test): * tests/stress/catch-parameter-destructuring.js: Added. (shouldBe): (shouldThrow): (prototype.call): (catch): (shouldThrow.try.throw.get error): (initialize): (array): (generator.gen): (generator): * tests/stress/catch-parameter-syntax.js: Added. (testSyntax): (testSyntaxError): * tests/stress/reserved-word-with-escape.js: (testSyntaxError.String.raw.a): (String.raw.SyntaxError.Cannot.use.the.keyword.string_appeared_here.as.a.name): * tests/stress/yield-named-variable.js: LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/mozilla/strict/12.14.1-expected.txt: * js/mozilla/strict/script-tests/12.14.1.js: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T10-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T13-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T5-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T7-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T8-expected.txt: Canonical link: https://commits.webkit.org/171405@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@195439 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-01-22 04:21:36 +00:00
SyntaxError: Cannot use the keyword 'in' as a lexical variable name.
SyntaxError: Cannot use the keyword 'in' as a lexical variable name.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: 'for (let in {}) {}'
[ES6] Catch parameter should accept BindingPattern https://bugs.webkit.org/show_bug.cgi?id=152385 Reviewed by Saam Barati. Source/JavaScriptCore: This patch implements destructuring in catch parameter. Catch parameter accepts binding pattern and binding identifier. It creates lexical bindings. And "yield" and "let" are specially handled as is the same to function parameters. In addition to that, we make destructuring parsing errors more descriptive. * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::emitPushCatchScope): * bytecompiler/BytecodeGenerator.h: * bytecompiler/NodesCodegen.cpp: (JSC::TryNode::emitBytecode): * parser/ASTBuilder.h: (JSC::ASTBuilder::createTryStatement): * parser/NodeConstructors.h: (JSC::TryNode::TryNode): * parser/Nodes.h: * parser/Parser.cpp: (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseBindingOrAssignmentElement): (JSC::destructuringKindToVariableKindName): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseFormalParameters): (JSC::Parser<LexerType>::parseFunctionParameters): * parser/Parser.h: (JSC::Parser::destructuringKindFromDeclarationType): * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createTryStatement): * tests/es6.yaml: * tests/es6/destructuring_in_catch_heads.js: Added. (test): * tests/stress/catch-parameter-destructuring.js: Added. (shouldBe): (shouldThrow): (prototype.call): (catch): (shouldThrow.try.throw.get error): (initialize): (array): (generator.gen): (generator): * tests/stress/catch-parameter-syntax.js: Added. (testSyntax): (testSyntaxError): * tests/stress/reserved-word-with-escape.js: (testSyntaxError.String.raw.a): (String.raw.SyntaxError.Cannot.use.the.keyword.string_appeared_here.as.a.name): * tests/stress/yield-named-variable.js: LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/mozilla/strict/12.14.1-expected.txt: * js/mozilla/strict/script-tests/12.14.1.js: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T10-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T13-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T5-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T7-expected.txt: * sputnik/Conformance/12_Statement/12.14_The_try_Statement/S12.14_A16_T8-expected.txt: Canonical link: https://commits.webkit.org/171405@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@195439 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-01-22 04:21:36 +00:00
SyntaxError: Cannot use the keyword 'in' as a lexical variable name.
SyntaxError: Cannot use the keyword 'in' as a lexical variable name.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; for (let in {}) {}'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'let x; var x;'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; let x; var x;'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'let x; var {x} = 20;'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; let x; var {x} = 20;'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'let x; var [x] = 20;'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; let x; var [x] = 20;'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
PASS Has syntax error: 'function f() { var x; let x; }'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
PASS Has syntax error: ''use strict'; function f() { var x; let x; }'
SyntaxError: Unexpected identifier 'x'. Cannot declare a lexical variable twice: 'x'.
SyntaxError: Unexpected identifier 'x'. Cannot declare a lexical variable twice: 'x'.
PASS Has syntax error: 'function f() { var x; let [x] = 20; }'
SyntaxError: Unexpected identifier 'x'. Cannot declare a lexical variable twice: 'x'.
SyntaxError: Unexpected identifier 'x'. Cannot declare a lexical variable twice: 'x'.
PASS Has syntax error: ''use strict'; function f() { var x; let [x] = 20; }'
SyntaxError: Unexpected identifier 'x'. Cannot declare a lexical variable twice: 'x'.
SyntaxError: Unexpected identifier 'x'. Cannot declare a lexical variable twice: 'x'.
PASS Has syntax error: 'function f() { var [x] = 20; let [x] = 20; }'
SyntaxError: Unexpected identifier 'x'. Cannot declare a lexical variable twice: 'x'.
SyntaxError: Unexpected identifier 'x'. Cannot declare a lexical variable twice: 'x'.
PASS Has syntax error: ''use strict'; function f() { var [x] = 20; let [x] = 20; }'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
PASS Has syntax error: 'function f() { var [x] = 20; let x; }'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
PASS Has syntax error: ''use strict'; function f() { var [x] = 20; let x; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'function f() { let x; var x; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; function f() { let x; var x; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'function f() { let x; var {x} = 20; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; function f() { let x; var {x} = 20; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'function f() { let x; var [x] = 20; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; function f() { let x; var [x] = 20; }'
ES6: Implement lexical scoping for function definitions in strict mode https://bugs.webkit.org/show_bug.cgi?id=152844 Reviewed by Geoffrey Garen. Source/JavaScriptCore: This patch implements block scoping for function definitions in strict mode. The implementation works as follows: - If we're in sloppy mode, function declarations work exactly as they did before this patch. I.e, function declarations are hoisted and declared like "var" variables. - If you're in strict mode and at the top of a function scope or program scope, function declarations still work like they used to. They are defined like "var" variables. This is necessary for backwards compatibility because ES5 strict mode allowed duplicate function declarations at the top-most scope of a program/function. - If you're in strict mode and inside a block statement or a switch statement, function declarations are now block scoped. All function declarations within a block are hoisted to the beginning of the block. They are not hoisted out of the block like they are in sloppy mode. This allows for the following types of programs: ``` function foo() { function bar() { return 20; } { function bar() { return 30; } bar(); // 30 } bar(); // 20 } ``` * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::instantiateLexicalVariables): (JSC::BytecodeGenerator::emitPrefillStackTDZVariables): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::pushLexicalScopeInternal): (JSC::BytecodeGenerator::initializeBlockScopedFunctions): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::pushTDZVariables): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewRegExp): (JSC::BytecodeGenerator::emitNewFunctionExpressionCommon): (JSC::BytecodeGenerator::emitNewFunctionExpression): (JSC::BytecodeGenerator::emitNewArrowFunctionExpression): * bytecompiler/BytecodeGenerator.h: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createFuncDeclStatement): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::funcDeclarations): Deleted. * parser/NodeConstructors.h: (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::BlockNode::BlockNode): * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::ModuleProgramNode::ModuleProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::VariableEnvironmentNode::functionStack): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::ModuleProgramNode::startColumn): (JSC::ModuleProgramNode::endColumn): (JSC::ScopeNode::functionStack): Deleted. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::getMetadata): (JSC::Parser<LexerType>::parseFunctionDeclaration): (JSC::Parser<LexerType>::parseExportDeclaration): * parser/Parser.h: (JSC::Scope::declareVariable): (JSC::Scope::declareFunction): (JSC::Scope::appendFunction): (JSC::Scope::takeFunctionDeclarations): (JSC::Scope::declareLexicalVariable): (JSC::Parser::currentVariableScope): (JSC::Parser::currentLexicalDeclarationScope): (JSC::Parser::currentFunctionScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::declareVariable): (JSC::Parser::declareFunction): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::isFunctionMetadataNode): (JSC::Parser<LexerType>::parse): * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createFuncDeclStatement): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): * parser/VariableEnvironment.h: (JSC::VariableEnvironmentEntry::isExported): (JSC::VariableEnvironmentEntry::isImported): (JSC::VariableEnvironmentEntry::isImportedNamespace): (JSC::VariableEnvironmentEntry::isFunction): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConst): (JSC::VariableEnvironmentEntry::setIsExported): (JSC::VariableEnvironmentEntry::setIsImported): (JSC::VariableEnvironmentEntry::setIsImportedNamespace): (JSC::VariableEnvironmentEntry::setIsFunction): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::VariableEnvironment): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): * tests/es6.yaml: * tests/stress/block-scoped-function-declarations.js: Added. (assert): (test): (f.foo.bar): (f.foo.): (f.foo): (f): (assert.foo.): (assert.foo): (assert.foo.foo): (assert.foo.bar): (assert.foo.switch.case.1): (assert.foo.switch.case.2): (assert.foo.switch.foo): (assert.foo.switch.bar): LayoutTests: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: * js/script-tests/parser-syntax-check.js: (testFailed): (runTest): Canonical link: https://commits.webkit.org/173389@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@197915 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-03-10 02:04:20 +00:00
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
PASS Has syntax error: 'function f() { let x; function x(){} }'
ES6: Implement lexical scoping for function definitions in strict mode https://bugs.webkit.org/show_bug.cgi?id=152844 Reviewed by Geoffrey Garen. Source/JavaScriptCore: This patch implements block scoping for function definitions in strict mode. The implementation works as follows: - If we're in sloppy mode, function declarations work exactly as they did before this patch. I.e, function declarations are hoisted and declared like "var" variables. - If you're in strict mode and at the top of a function scope or program scope, function declarations still work like they used to. They are defined like "var" variables. This is necessary for backwards compatibility because ES5 strict mode allowed duplicate function declarations at the top-most scope of a program/function. - If you're in strict mode and inside a block statement or a switch statement, function declarations are now block scoped. All function declarations within a block are hoisted to the beginning of the block. They are not hoisted out of the block like they are in sloppy mode. This allows for the following types of programs: ``` function foo() { function bar() { return 20; } { function bar() { return 30; } bar(); // 30 } bar(); // 20 } ``` * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::instantiateLexicalVariables): (JSC::BytecodeGenerator::emitPrefillStackTDZVariables): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::pushLexicalScopeInternal): (JSC::BytecodeGenerator::initializeBlockScopedFunctions): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::pushTDZVariables): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewRegExp): (JSC::BytecodeGenerator::emitNewFunctionExpressionCommon): (JSC::BytecodeGenerator::emitNewFunctionExpression): (JSC::BytecodeGenerator::emitNewArrowFunctionExpression): * bytecompiler/BytecodeGenerator.h: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createFuncDeclStatement): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::funcDeclarations): Deleted. * parser/NodeConstructors.h: (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::BlockNode::BlockNode): * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::ModuleProgramNode::ModuleProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::VariableEnvironmentNode::functionStack): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::ModuleProgramNode::startColumn): (JSC::ModuleProgramNode::endColumn): (JSC::ScopeNode::functionStack): Deleted. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::getMetadata): (JSC::Parser<LexerType>::parseFunctionDeclaration): (JSC::Parser<LexerType>::parseExportDeclaration): * parser/Parser.h: (JSC::Scope::declareVariable): (JSC::Scope::declareFunction): (JSC::Scope::appendFunction): (JSC::Scope::takeFunctionDeclarations): (JSC::Scope::declareLexicalVariable): (JSC::Parser::currentVariableScope): (JSC::Parser::currentLexicalDeclarationScope): (JSC::Parser::currentFunctionScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::declareVariable): (JSC::Parser::declareFunction): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::isFunctionMetadataNode): (JSC::Parser<LexerType>::parse): * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createFuncDeclStatement): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): * parser/VariableEnvironment.h: (JSC::VariableEnvironmentEntry::isExported): (JSC::VariableEnvironmentEntry::isImported): (JSC::VariableEnvironmentEntry::isImportedNamespace): (JSC::VariableEnvironmentEntry::isFunction): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConst): (JSC::VariableEnvironmentEntry::setIsExported): (JSC::VariableEnvironmentEntry::setIsImported): (JSC::VariableEnvironmentEntry::setIsImportedNamespace): (JSC::VariableEnvironmentEntry::setIsFunction): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::VariableEnvironment): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): * tests/es6.yaml: * tests/stress/block-scoped-function-declarations.js: Added. (assert): (test): (f.foo.bar): (f.foo.): (f.foo): (f): (assert.foo.): (assert.foo): (assert.foo.foo): (assert.foo.bar): (assert.foo.switch.case.1): (assert.foo.switch.case.2): (assert.foo.switch.foo): (assert.foo.switch.bar): LayoutTests: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: * js/script-tests/parser-syntax-check.js: (testFailed): (runTest): Canonical link: https://commits.webkit.org/173389@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@197915 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-03-10 02:04:20 +00:00
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
PASS Has syntax error: ''use strict'; function f() { let x; function x(){} }'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
PASS Has syntax error: 'function f() { function x(){}; let x; }'
SyntaxError: Cannot declare a let variable twice: 'x'.
SyntaxError: Cannot declare a let variable twice: 'x'.
PASS Has syntax error: ''use strict'; function f() { function x(){}; let x; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'function f() { const x = 20; var x; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; function f() { const x = 20; var x; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'function f() { const x = 20; var {x} = 20; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; function f() { const x = 20; var {x} = 20; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'function f() { const x = 20; var [x] = 20; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; function f() { const x = 20; var [x] = 20; }'
ES6: Implement lexical scoping for function definitions in strict mode https://bugs.webkit.org/show_bug.cgi?id=152844 Reviewed by Geoffrey Garen. Source/JavaScriptCore: This patch implements block scoping for function definitions in strict mode. The implementation works as follows: - If we're in sloppy mode, function declarations work exactly as they did before this patch. I.e, function declarations are hoisted and declared like "var" variables. - If you're in strict mode and at the top of a function scope or program scope, function declarations still work like they used to. They are defined like "var" variables. This is necessary for backwards compatibility because ES5 strict mode allowed duplicate function declarations at the top-most scope of a program/function. - If you're in strict mode and inside a block statement or a switch statement, function declarations are now block scoped. All function declarations within a block are hoisted to the beginning of the block. They are not hoisted out of the block like they are in sloppy mode. This allows for the following types of programs: ``` function foo() { function bar() { return 20; } { function bar() { return 30; } bar(); // 30 } bar(); // 20 } ``` * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::instantiateLexicalVariables): (JSC::BytecodeGenerator::emitPrefillStackTDZVariables): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::pushLexicalScopeInternal): (JSC::BytecodeGenerator::initializeBlockScopedFunctions): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::pushTDZVariables): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewRegExp): (JSC::BytecodeGenerator::emitNewFunctionExpressionCommon): (JSC::BytecodeGenerator::emitNewFunctionExpression): (JSC::BytecodeGenerator::emitNewArrowFunctionExpression): * bytecompiler/BytecodeGenerator.h: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createFuncDeclStatement): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::funcDeclarations): Deleted. * parser/NodeConstructors.h: (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::BlockNode::BlockNode): * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::ModuleProgramNode::ModuleProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::VariableEnvironmentNode::functionStack): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::ModuleProgramNode::startColumn): (JSC::ModuleProgramNode::endColumn): (JSC::ScopeNode::functionStack): Deleted. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::getMetadata): (JSC::Parser<LexerType>::parseFunctionDeclaration): (JSC::Parser<LexerType>::parseExportDeclaration): * parser/Parser.h: (JSC::Scope::declareVariable): (JSC::Scope::declareFunction): (JSC::Scope::appendFunction): (JSC::Scope::takeFunctionDeclarations): (JSC::Scope::declareLexicalVariable): (JSC::Parser::currentVariableScope): (JSC::Parser::currentLexicalDeclarationScope): (JSC::Parser::currentFunctionScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::declareVariable): (JSC::Parser::declareFunction): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::isFunctionMetadataNode): (JSC::Parser<LexerType>::parse): * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createFuncDeclStatement): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): * parser/VariableEnvironment.h: (JSC::VariableEnvironmentEntry::isExported): (JSC::VariableEnvironmentEntry::isImported): (JSC::VariableEnvironmentEntry::isImportedNamespace): (JSC::VariableEnvironmentEntry::isFunction): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConst): (JSC::VariableEnvironmentEntry::setIsExported): (JSC::VariableEnvironmentEntry::setIsImported): (JSC::VariableEnvironmentEntry::setIsImportedNamespace): (JSC::VariableEnvironmentEntry::setIsFunction): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::VariableEnvironment): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): * tests/es6.yaml: * tests/stress/block-scoped-function-declarations.js: Added. (assert): (test): (f.foo.bar): (f.foo.): (f.foo): (f): (assert.foo.): (assert.foo): (assert.foo.foo): (assert.foo.bar): (assert.foo.switch.case.1): (assert.foo.switch.case.2): (assert.foo.switch.foo): (assert.foo.switch.bar): LayoutTests: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: * js/script-tests/parser-syntax-check.js: (testFailed): (runTest): Canonical link: https://commits.webkit.org/173389@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@197915 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-03-10 02:04:20 +00:00
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
PASS Has syntax error: 'function f() { const x = 20; function x(){} }'
ES6: Implement lexical scoping for function definitions in strict mode https://bugs.webkit.org/show_bug.cgi?id=152844 Reviewed by Geoffrey Garen. Source/JavaScriptCore: This patch implements block scoping for function definitions in strict mode. The implementation works as follows: - If we're in sloppy mode, function declarations work exactly as they did before this patch. I.e, function declarations are hoisted and declared like "var" variables. - If you're in strict mode and at the top of a function scope or program scope, function declarations still work like they used to. They are defined like "var" variables. This is necessary for backwards compatibility because ES5 strict mode allowed duplicate function declarations at the top-most scope of a program/function. - If you're in strict mode and inside a block statement or a switch statement, function declarations are now block scoped. All function declarations within a block are hoisted to the beginning of the block. They are not hoisted out of the block like they are in sloppy mode. This allows for the following types of programs: ``` function foo() { function bar() { return 20; } { function bar() { return 30; } bar(); // 30 } bar(); // 20 } ``` * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::instantiateLexicalVariables): (JSC::BytecodeGenerator::emitPrefillStackTDZVariables): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::pushLexicalScopeInternal): (JSC::BytecodeGenerator::initializeBlockScopedFunctions): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::pushTDZVariables): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewRegExp): (JSC::BytecodeGenerator::emitNewFunctionExpressionCommon): (JSC::BytecodeGenerator::emitNewFunctionExpression): (JSC::BytecodeGenerator::emitNewArrowFunctionExpression): * bytecompiler/BytecodeGenerator.h: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createFuncDeclStatement): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::funcDeclarations): Deleted. * parser/NodeConstructors.h: (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::BlockNode::BlockNode): * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::ModuleProgramNode::ModuleProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::VariableEnvironmentNode::functionStack): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::ModuleProgramNode::startColumn): (JSC::ModuleProgramNode::endColumn): (JSC::ScopeNode::functionStack): Deleted. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::getMetadata): (JSC::Parser<LexerType>::parseFunctionDeclaration): (JSC::Parser<LexerType>::parseExportDeclaration): * parser/Parser.h: (JSC::Scope::declareVariable): (JSC::Scope::declareFunction): (JSC::Scope::appendFunction): (JSC::Scope::takeFunctionDeclarations): (JSC::Scope::declareLexicalVariable): (JSC::Parser::currentVariableScope): (JSC::Parser::currentLexicalDeclarationScope): (JSC::Parser::currentFunctionScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::declareVariable): (JSC::Parser::declareFunction): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::isFunctionMetadataNode): (JSC::Parser<LexerType>::parse): * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createFuncDeclStatement): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): * parser/VariableEnvironment.h: (JSC::VariableEnvironmentEntry::isExported): (JSC::VariableEnvironmentEntry::isImported): (JSC::VariableEnvironmentEntry::isImportedNamespace): (JSC::VariableEnvironmentEntry::isFunction): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConst): (JSC::VariableEnvironmentEntry::setIsExported): (JSC::VariableEnvironmentEntry::setIsImported): (JSC::VariableEnvironmentEntry::setIsImportedNamespace): (JSC::VariableEnvironmentEntry::setIsFunction): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::VariableEnvironment): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): * tests/es6.yaml: * tests/stress/block-scoped-function-declarations.js: Added. (assert): (test): (f.foo.bar): (f.foo.): (f.foo): (f): (assert.foo.): (assert.foo): (assert.foo.foo): (assert.foo.bar): (assert.foo.switch.case.1): (assert.foo.switch.case.2): (assert.foo.switch.foo): (assert.foo.switch.bar): LayoutTests: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: * js/script-tests/parser-syntax-check.js: (testFailed): (runTest): Canonical link: https://commits.webkit.org/173389@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@197915 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-03-10 02:04:20 +00:00
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
PASS Has syntax error: ''use strict'; function f() { const x = 20; function x(){} }'
SyntaxError: Cannot declare a const variable twice: 'x'.
SyntaxError: Cannot declare a const variable twice: 'x'.
PASS Has syntax error: 'function f() { function x(){}; const x = 20; }'
SyntaxError: Cannot declare a const variable twice: 'x'.
SyntaxError: Cannot declare a const variable twice: 'x'.
PASS Has syntax error: ''use strict'; function f() { function x(){}; const x = 20; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'function f() { class x{}; var x; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; function f() { class x{}; var x; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'function f() { class x{}; var {x} = 20; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; function f() { class x{}; var {x} = 20; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: 'function f() { class x{}; var [x] = 20; }'
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
SyntaxError: Cannot declare a var variable that shadows a let/const/class variable: 'x'.
PASS Has syntax error: ''use strict'; function f() { class x{}; var [x] = 20; }'
ES6: Implement lexical scoping for function definitions in strict mode https://bugs.webkit.org/show_bug.cgi?id=152844 Reviewed by Geoffrey Garen. Source/JavaScriptCore: This patch implements block scoping for function definitions in strict mode. The implementation works as follows: - If we're in sloppy mode, function declarations work exactly as they did before this patch. I.e, function declarations are hoisted and declared like "var" variables. - If you're in strict mode and at the top of a function scope or program scope, function declarations still work like they used to. They are defined like "var" variables. This is necessary for backwards compatibility because ES5 strict mode allowed duplicate function declarations at the top-most scope of a program/function. - If you're in strict mode and inside a block statement or a switch statement, function declarations are now block scoped. All function declarations within a block are hoisted to the beginning of the block. They are not hoisted out of the block like they are in sloppy mode. This allows for the following types of programs: ``` function foo() { function bar() { return 20; } { function bar() { return 30; } bar(); // 30 } bar(); // 20 } ``` * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::instantiateLexicalVariables): (JSC::BytecodeGenerator::emitPrefillStackTDZVariables): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::pushLexicalScopeInternal): (JSC::BytecodeGenerator::initializeBlockScopedFunctions): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::pushTDZVariables): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewRegExp): (JSC::BytecodeGenerator::emitNewFunctionExpressionCommon): (JSC::BytecodeGenerator::emitNewFunctionExpression): (JSC::BytecodeGenerator::emitNewArrowFunctionExpression): * bytecompiler/BytecodeGenerator.h: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createFuncDeclStatement): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::funcDeclarations): Deleted. * parser/NodeConstructors.h: (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::BlockNode::BlockNode): * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::ModuleProgramNode::ModuleProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::VariableEnvironmentNode::functionStack): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::ModuleProgramNode::startColumn): (JSC::ModuleProgramNode::endColumn): (JSC::ScopeNode::functionStack): Deleted. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::getMetadata): (JSC::Parser<LexerType>::parseFunctionDeclaration): (JSC::Parser<LexerType>::parseExportDeclaration): * parser/Parser.h: (JSC::Scope::declareVariable): (JSC::Scope::declareFunction): (JSC::Scope::appendFunction): (JSC::Scope::takeFunctionDeclarations): (JSC::Scope::declareLexicalVariable): (JSC::Parser::currentVariableScope): (JSC::Parser::currentLexicalDeclarationScope): (JSC::Parser::currentFunctionScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::declareVariable): (JSC::Parser::declareFunction): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::isFunctionMetadataNode): (JSC::Parser<LexerType>::parse): * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createFuncDeclStatement): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): * parser/VariableEnvironment.h: (JSC::VariableEnvironmentEntry::isExported): (JSC::VariableEnvironmentEntry::isImported): (JSC::VariableEnvironmentEntry::isImportedNamespace): (JSC::VariableEnvironmentEntry::isFunction): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConst): (JSC::VariableEnvironmentEntry::setIsExported): (JSC::VariableEnvironmentEntry::setIsImported): (JSC::VariableEnvironmentEntry::setIsImportedNamespace): (JSC::VariableEnvironmentEntry::setIsFunction): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::VariableEnvironment): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): * tests/es6.yaml: * tests/stress/block-scoped-function-declarations.js: Added. (assert): (test): (f.foo.bar): (f.foo.): (f.foo): (f): (assert.foo.): (assert.foo): (assert.foo.foo): (assert.foo.bar): (assert.foo.switch.case.1): (assert.foo.switch.case.2): (assert.foo.switch.foo): (assert.foo.switch.bar): LayoutTests: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: * js/script-tests/parser-syntax-check.js: (testFailed): (runTest): Canonical link: https://commits.webkit.org/173389@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@197915 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-03-10 02:04:20 +00:00
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
PASS Has syntax error: 'function f() { class x{}; function x(){} }'
ES6: Implement lexical scoping for function definitions in strict mode https://bugs.webkit.org/show_bug.cgi?id=152844 Reviewed by Geoffrey Garen. Source/JavaScriptCore: This patch implements block scoping for function definitions in strict mode. The implementation works as follows: - If we're in sloppy mode, function declarations work exactly as they did before this patch. I.e, function declarations are hoisted and declared like "var" variables. - If you're in strict mode and at the top of a function scope or program scope, function declarations still work like they used to. They are defined like "var" variables. This is necessary for backwards compatibility because ES5 strict mode allowed duplicate function declarations at the top-most scope of a program/function. - If you're in strict mode and inside a block statement or a switch statement, function declarations are now block scoped. All function declarations within a block are hoisted to the beginning of the block. They are not hoisted out of the block like they are in sloppy mode. This allows for the following types of programs: ``` function foo() { function bar() { return 20; } { function bar() { return 30; } bar(); // 30 } bar(); // 20 } ``` * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::instantiateLexicalVariables): (JSC::BytecodeGenerator::emitPrefillStackTDZVariables): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::pushLexicalScopeInternal): (JSC::BytecodeGenerator::initializeBlockScopedFunctions): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::pushTDZVariables): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewRegExp): (JSC::BytecodeGenerator::emitNewFunctionExpressionCommon): (JSC::BytecodeGenerator::emitNewFunctionExpression): (JSC::BytecodeGenerator::emitNewArrowFunctionExpression): * bytecompiler/BytecodeGenerator.h: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createFuncDeclStatement): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::funcDeclarations): Deleted. * parser/NodeConstructors.h: (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::BlockNode::BlockNode): * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::ModuleProgramNode::ModuleProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::VariableEnvironmentNode::functionStack): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::ModuleProgramNode::startColumn): (JSC::ModuleProgramNode::endColumn): (JSC::ScopeNode::functionStack): Deleted. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::getMetadata): (JSC::Parser<LexerType>::parseFunctionDeclaration): (JSC::Parser<LexerType>::parseExportDeclaration): * parser/Parser.h: (JSC::Scope::declareVariable): (JSC::Scope::declareFunction): (JSC::Scope::appendFunction): (JSC::Scope::takeFunctionDeclarations): (JSC::Scope::declareLexicalVariable): (JSC::Parser::currentVariableScope): (JSC::Parser::currentLexicalDeclarationScope): (JSC::Parser::currentFunctionScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::declareVariable): (JSC::Parser::declareFunction): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::isFunctionMetadataNode): (JSC::Parser<LexerType>::parse): * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createFuncDeclStatement): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): * parser/VariableEnvironment.h: (JSC::VariableEnvironmentEntry::isExported): (JSC::VariableEnvironmentEntry::isImported): (JSC::VariableEnvironmentEntry::isImportedNamespace): (JSC::VariableEnvironmentEntry::isFunction): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConst): (JSC::VariableEnvironmentEntry::setIsExported): (JSC::VariableEnvironmentEntry::setIsImported): (JSC::VariableEnvironmentEntry::setIsImportedNamespace): (JSC::VariableEnvironmentEntry::setIsFunction): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::VariableEnvironment): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): * tests/es6.yaml: * tests/stress/block-scoped-function-declarations.js: Added. (assert): (test): (f.foo.bar): (f.foo.): (f.foo): (f): (assert.foo.): (assert.foo): (assert.foo.foo): (assert.foo.bar): (assert.foo.switch.case.1): (assert.foo.switch.case.2): (assert.foo.switch.foo): (assert.foo.switch.bar): LayoutTests: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: * js/script-tests/parser-syntax-check.js: (testFailed): (runTest): Canonical link: https://commits.webkit.org/173389@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@197915 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-03-10 02:04:20 +00:00
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
SyntaxError: Cannot declare a function that shadows a let/const/class/function variable 'x' in strict mode.
PASS Has syntax error: ''use strict'; function f() { class x{}; function x(){} }'
SyntaxError: Cannot declare a class twice: 'x'.
SyntaxError: Cannot declare a class twice: 'x'.
PASS Has syntax error: 'function f() { function x(){}; class x{}; }'
SyntaxError: Cannot declare a class twice: 'x'.
SyntaxError: Cannot declare a class twice: 'x'.
PASS Has syntax error: ''use strict'; function f() { function x(){}; class x{}; }'
SyntaxError: Unexpected identifier 'y'
SyntaxError: Unexpected identifier 'y'
PASS Has syntax error: 'let x; with ({}) let y = 3;'
ReferenceError: Can't find variable: let
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Does not have syntax error: 'let;'
SyntaxError: Unexpected token ';'. Expected a parameter pattern or a ')' in parameter list.
SyntaxError: Unexpected token ';'. Expected a parameter pattern or a ')' in parameter list.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let;'
PASS Does not have syntax error: 'var let;'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Cannot use 'let' as a variable name in strict mode.
SyntaxError: Cannot use 'let' as a variable name in strict mode.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; var let;'
PASS Does not have syntax error: 'var {let} = 40;'
SyntaxError: Cannot use abbreviated destructuring syntax for keyword 'let'.
SyntaxError: Cannot use abbreviated destructuring syntax for keyword 'let'.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; var {let} = 40;'
ASSERTION FAILED: inIndex != notFound in JSC::invalidParameterInSourceAppender() https://bugs.webkit.org/show_bug.cgi?id=170924 <rdar://problem/31721052> Reviewed by Mark Lam. JSTests: * stress/error-message-for-function-base-not-found.js: Added. (assert): (throw.new.Error): * stress/error-messages-for-in-operator-should-not-crash.js: Added. (catch): LayoutTests/imported/w3c: * web-platform-tests/css-timing-1/cubic-bezier-timing-functions-output-expected.txt: * web-platform-tests/css-timing-1/frames-timing-functions-output-expected.txt: * web-platform-tests/css-timing-1/step-timing-functions-output-expected.txt: Source/JavaScriptCore: The error message handler for "in" was searching for the literal string "in". However, our parser incorrectly allows escaped characters to be part of keywords. So this is parsed as "in" in JSC: "i\u006E". It should not be parsed that way. I opened https://bugs.webkit.org/show_bug.cgi?id=171310 to address this issue. Regardless, the error message handlers should handle unexpected text gracefully. All functions that try to augment error messages with the goal of providing a more textual context for the error message should use the original error message instead of crashing when they detect unexpected text. This patch also changes the already buggy code that tries to find the base of a function call. That could would fail for code like this: "zoo.bar("/abc\)*/");". See https://bugs.webkit.org/show_bug.cgi?id=146304 It would think that the base is "z". However, the algorithm that tries to find the base can often tell when it fails, and when it does, it should happily return the approximate text error message instead of thinking that the base is "z". * runtime/ExceptionHelpers.cpp: (JSC::functionCallBase): (JSC::notAFunctionSourceAppender): (JSC::invalidParameterInSourceAppender): LayoutTests: * js/let-syntax-expected.txt: Canonical link: https://commits.webkit.org/188248@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215852 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-27 02:28:39 +00:00
TypeError: undefined is not a function (near '...[let]...')
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Does not have syntax error: 'var [let] = 40;'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Cannot use 'let' as a variable name in strict mode.
SyntaxError: Cannot use 'let' as a variable name in strict mode.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; var [let] = 40;'
PASS Does not have syntax error: 'var {p: let} = 40;'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Cannot use 'let' as a variable name in strict mode.
SyntaxError: Cannot use 'let' as a variable name in strict mode.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; var {p: let} = 40;'
PASS Does not have syntax error: '(function test(let){});'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Cannot use 'let' as a parameter name in strict mode.
SyntaxError: Cannot use 'let' as a parameter name in strict mode.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; (function test(let){});'
PASS Does not have syntax error: 'let: for (v of []) break let;'
SyntaxError: Unexpected token ':'. Expected a parameter pattern or a ')' in parameter list.
SyntaxError: Unexpected token ':'. Expected a parameter pattern or a ')' in parameter list.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let: for (v of []) break let;'
PASS Does not have syntax error: 'let: for (v of []) continue let;'
SyntaxError: Unexpected token ':'. Expected a parameter pattern or a ')' in parameter list.
SyntaxError: Unexpected token ':'. Expected a parameter pattern or a ')' in parameter list.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let: for (v of []) continue let;'
PASS Does not have syntax error: 'let: for (v in {}) break;'
SyntaxError: Unexpected token ':'. Expected a parameter pattern or a ')' in parameter list.
SyntaxError: Unexpected token ':'. Expected a parameter pattern or a ')' in parameter list.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let: for (v in {}) break;'
PASS Does not have syntax error: 'let: for (v in {}) break;'
SyntaxError: Unexpected token ':'. Expected a parameter pattern or a ')' in parameter list.
SyntaxError: Unexpected token ':'. Expected a parameter pattern or a ')' in parameter list.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let: for (v in {}) break;'
PASS Does not have syntax error: 'let: for (var v = 0; false; ) {};'
SyntaxError: Unexpected token ':'. Expected a parameter pattern or a ')' in parameter list.
SyntaxError: Unexpected token ':'. Expected a parameter pattern or a ')' in parameter list.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; let: for (var v = 0; false; ) {};'
PASS Does not have syntax error: 'try { } catch(let) {}'
test262: test262/test/language/expressions/generators/yield-as-label.js https://bugs.webkit.org/show_bug.cgi?id=170979 Patch by Joseph Pecoraro <pecoraro@apple.com> on 2017-04-24 Reviewed by Saam Barati. JSTests: * stress/async-await-module-reserved-word.js: * stress/async-await-reserved-word.js: Converge on "Cannot" instead of "Can't". * catch-parameter-syntax.js: * yield-named-variable-generator.js: * yield-named-variable.js: * stress/yield-label-generator.js: * stress/yield-label.js: * stress/yield-reserved-word.js: Added. More complete list of when 'yield' is allowed. * ChakraCore/test/strict/23.reservedWords_sm.baseline-jsc: * test262.yaml: Source/JavaScriptCore: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseFormalParameters): Converge on "Cannot" instead of "Can't" in error messages. (JSC::Parser<LexerType>::parseFunctionInfo): Disallow "yield" as the generator function name in function expressions. This refers to the difference between Declaration and Expression, where only GeneratorExpression explicitly has [+Yield] disallowing yield for the generator name: GeneratorDeclaration[Yield, Await, Default]: function * BindingIdentifier[?Yield, ?Await] ... GeneratorExpression: function * BindingIdentifier[+Yield, ~Await]opt ... (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): Disallow "yield" as a label name in strict mode or inside a generator. (JSC::Parser<LexerType>::parseProperty): Disallow "yield" or any keyword in object literal shorthands. * parser/Parser.h: (JSC::Parser::getToken): (JSC::Parser::isDisallowedIdentifierLet): (JSC::Parser::isDisallowedIdentifierYield): (JSC::Parser::disallowedIdentifierLetReason): (JSC::Parser::disallowedIdentifierYieldReason): Follow pattern for improved error messages based on context. LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: Extend this test to cover object literal shorthand with keywords. * js/dom/reserved-words-as-property-expected.txt: * js/let-syntax-expected.txt: * js/parser-syntax-check-expected.txt: Improved error messages. Canonical link: https://commits.webkit.org/188086@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2017-04-24 16:06:33 +00:00
SyntaxError: Cannot use 'let' as a catch parameter name in strict mode.
SyntaxError: Cannot use 'let' as a catch parameter name in strict mode.
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS Has syntax error: ''use strict'; try { } catch(let) {}'
PASS Does not have syntax error: 'let x; if (true) let: x = 3;'
SyntaxError: Unexpected keyword 'let'
SyntaxError: Unexpected keyword 'let'
PASS Has syntax error: ''use strict'; let x; if (true) let: x = 3;'
[ES6] implement block scoping to enable 'let' https://bugs.webkit.org/show_bug.cgi?id=142944 Reviewed by Filip Pizlo. Source/JavaScriptCore: * CMakeLists.txt: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: * JavaScriptCore.xcodeproj/project.pbxproj: * builtins/BuiltinExecutables.cpp: (JSC::BuiltinExecutables::createExecutableInternal): * bytecode/BytecodeList.json: This patch adds a new opcode and removes op_pop_scope: 1) op_get_parent_scope returns the parent scope but doesn't implicitly write that scope into the scope register. op_pop_scope is now reduced to op_get_parent_scope followed by op_mov. * bytecode/BytecodeUseDef.h: (JSC::computeUsesForBytecodeOffset): (JSC::computeDefsForBytecodeOffset): * bytecode/CodeBlock.cpp: (JSC::CodeBlock::dumpBytecode): (JSC::CodeBlock::CodeBlock): (JSC::CodeBlock::stronglyVisitStrongReferences): * bytecode/CodeBlock.h: (JSC::CodeBlock::addStringSwitchJumpTable): (JSC::CodeBlock::stringSwitchJumpTable): (JSC::CodeBlock::symbolTable): (JSC::CodeBlock::evalCodeCache): (JSC::CodeBlock::setConstantRegisters): (JSC::CodeBlock::replaceConstant): op_put_to_scope for LocalClosureVar now takes as an argument the constant index for the Symbol Table it will be putting into. This argument is only used to communicate from the BytecodeGenerator to CodeBlock linking time and it is not present in the linked bytecode. op_put_to_scope for non LocalClosureVar takes, at the same index, an argument that represents the local scope depth which it uses for JSScope::abstractResolve to know how many scopes it needs to skip. Again, this is not in the linked code. op_get_from_scope and op_resolve_scope also take as an argument the local scope depth to use in JSScope::abstractResolve. Again, this is not used in the linked code. * bytecode/EvalCodeCache.h: (JSC::EvalCodeCache::tryGet): (JSC::EvalCodeCache::getSlow): (JSC::EvalCodeCache::clear): (JSC::EvalCodeCache::isCacheable): When direct eval is called and passed a scope that corresponds to a lexical scope, we can't safely cache that code because we won't be able to guarantee that the cached code is always executed in the same scope. Consider this example: function foo() { let x = 20; eval("x;"); if (b) { let x = 30; if (b) { let y = 40; eval("x;") } } } We can't reuse resolution depth when linking get_from_scope in evals. * bytecode/UnlinkedCodeBlock.cpp: (JSC::generateFunctionCodeBlock): (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable): (JSC::UnlinkedFunctionExecutable::parameterCount): * bytecode/UnlinkedCodeBlock.h: Unlinked functions now know the variables that were under TDZ in their parent scope. (JSC::UnlinkedCodeBlock::symbolTable): (JSC::UnlinkedCodeBlock::setSymbolTable): (JSC::UnlinkedCodeBlock::setSymbolTableConstantIndex): (JSC::UnlinkedCodeBlock::symbolTableConstantIndex): (JSC::UnlinkedCodeBlock::vm): * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::~BytecodeGenerator): (JSC::BytecodeGenerator::newRegister): (JSC::BytecodeGenerator::reclaimFreeRegisters): (JSC::BytecodeGenerator::newBlockScopeVariable): (JSC::BytecodeGenerator::newTemporary): (JSC::BytecodeGenerator::emitProfileType): (JSC::BytecodeGenerator::emitLoadGlobalObject): (JSC::BytecodeGenerator::pushLexicalScope): (JSC::BytecodeGenerator::popLexicalScope): (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration): (JSC::BytecodeGenerator::variable): (JSC::BytecodeGenerator::variablePerSymbolTable): (JSC::BytecodeGenerator::variableForLocalEntry): (JSC::BytecodeGenerator::createVariable): (JSC::BytecodeGenerator::emitResolveScope): (JSC::BytecodeGenerator::emitGetFromScope): (JSC::BytecodeGenerator::emitPutToScope): (JSC::BytecodeGenerator::initializeVariable): (JSC::BytecodeGenerator::emitTDZCheck): (JSC::BytecodeGenerator::needsTDZCheck): (JSC::BytecodeGenerator::emitTDZCheckIfNecessary): (JSC::BytecodeGenerator::liftTDZCheckIfPossible): (JSC::BytecodeGenerator::getVariablesUnderTDZ): (JSC::BytecodeGenerator::emitNewObject): (JSC::BytecodeGenerator::emitPushWithScope): (JSC::BytecodeGenerator::emitGetParentScope): (JSC::BytecodeGenerator::emitPopScope): (JSC::BytecodeGenerator::emitDebugHook): (JSC::BytecodeGenerator::pushFinallyContext): (JSC::BytecodeGenerator::pushIteratorCloseContext): (JSC::BytecodeGenerator::emitComplexPopScopes): (JSC::BytecodeGenerator::emitPopScopes): (JSC::BytecodeGenerator::popTryAndEmitCatch): (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler): (JSC::BytecodeGenerator::currentScopeDepth): (JSC::BytecodeGenerator::emitThrowReferenceError): (JSC::BytecodeGenerator::emitPushCatchScope): (JSC::BytecodeGenerator::beginSwitch): (JSC::BytecodeGenerator::emitReadOnlyExceptionIfNeeded): (JSC::BytecodeGenerator::emitEnumeration): * bytecompiler/BytecodeGenerator.h: (JSC::Variable::Variable): (JSC::Variable::isResolved): (JSC::Variable::symbolTableConstantIndex): (JSC::Variable::ident): (JSC::BytecodeGenerator::ignoredResult): (JSC::BytecodeGenerator::tempDestination): (JSC::BytecodeGenerator::lastOpcodeID): (JSC::BytecodeGenerator::makeFunction): (JSC::BytecodeGenerator::symbolTable): (JSC::BytecodeGenerator::shouldOptimizeLocals): Deleted. (JSC::BytecodeGenerator::canOptimizeNonLocals): Deleted. The heart of the changes in this patch are in the bytecode generator. The bytecode generator now keeps a stack of tuples of {symbol table, scope register, flag indicating catch or with scope, symbol table index in constant pool} that models the runtime scope stack. This symbol table stack is used in resolving local variables. Also, the bytecode generator handles pushing and popping of lexical scopes. This is relatively straight forward: Captured 'let' variables end up in the JSLexicalEnvironment scope and non-captured variables end up on the stack. Some trickiness is involved in generating code for 'for' loops that have captured variables (I'm talking about variables in the loop header, not the loop body). Each iteration of the for loop ends up with its own JSLexicalEnvironment. Static code must be generated in such a way to create this runtime behavior. This is done by emitting instructions to push and pop a lexical scope at the end of each loop and copying values from the previous loop's scope into the new scope. This code must also ensure that each loop iteration's scope refers to the same underlying SymbolTable so that no scope is accidentally mistaken as being a singleton scope. When the debugger is enabled, all lexically defined variables will end up in the JSLexicalEnvironment. * bytecompiler/NodesCodegen.cpp: (JSC::ResolveNode::emitBytecode): (JSC::FunctionCallResolveNode::emitBytecode): (JSC::PostfixNode::emitResolve): (JSC::DeleteResolveNode::emitBytecode): (JSC::TypeOfResolveNode::emitBytecode): (JSC::PrefixNode::emitResolve): (JSC::ReadModifyResolveNode::emitBytecode): (JSC::AssignResolveNode::emitBytecode): (JSC::BlockNode::emitBytecode): (JSC::ExprStatementNode::emitBytecode): (JSC::DeclarationStatement::emitBytecode): (JSC::EmptyVarExpression::emitBytecode): (JSC::EmptyLetExpression::emitBytecode): (JSC::ForNode::emitBytecode): (JSC::ForInNode::emitMultiLoopBytecode): (JSC::ForOfNode::emitBytecode): (JSC::SwitchNode::emitBytecode): (JSC::BindingNode::bindValue): (JSC::VarStatementNode::emitBytecode): Deleted. * debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): * debugger/DebuggerScope.cpp: (JSC::DebuggerScope::getOwnPropertySlot): (JSC::DebuggerScope::put): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGNode.h: (JSC::DFG::Node::castConstant): (JSC::DFG::Node::initializationValueForActivation): (JSC::DFG::Node::containsMovHint): * dfg/DFGObjectAllocationSinkingPhase.cpp: CreateActivation nodes now have a second OpInfo that tracks the initial value that needs to be placed in the activation. This initial value is also used in allocation sinking to create proper bottom values for all scope variables. * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCreateActivation): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * ftl/FTLIntrinsicRepository.h: * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateActivation): (JSC::FTL::DFG::LowerDFGToLLVM::compileMaterializeCreateActivation): * ftl/FTLOperations.cpp: (JSC::FTL::operationMaterializeObjectInOSR): * interpreter/Interpreter.cpp: (JSC::Interpreter::execute): * jit/CCallHelpers.h: (JSC::CCallHelpers::setupArgumentsWithExecState): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITInlines.h: (JSC::JIT::callOperation): * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::compileOpStrictEq): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_push_with_scope): (JSC::JIT::emit_op_to_number): (JSC::JIT::emit_op_catch): (JSC::JIT::emit_op_create_lexical_environment): (JSC::JIT::emit_op_get_parent_scope): (JSC::JIT::emit_op_switch_imm): (JSC::JIT::emit_op_enter): (JSC::JIT::emit_op_get_scope): (JSC::JIT::emit_op_pop_scope): Deleted. * jit/JITOperations.cpp: (JSC::canAccessArgumentIndexQuickly): * jit/JITOperations.h: * llint/LLIntSlowPaths.cpp: (JSC::LLInt::LLINT_SLOW_PATH_DECL): * llint/LLIntSlowPaths.h: * llint/LowLevelInterpreter.asm: * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * parser/ASTBuilder.h: (JSC::ASTBuilder::createSourceElements): (JSC::ASTBuilder::funcDeclarations): (JSC::ASTBuilder::features): (JSC::ASTBuilder::numConstants): (JSC::ASTBuilder::createConditionalExpr): (JSC::ASTBuilder::createAssignResolve): (JSC::ASTBuilder::createClassDeclStatement): (JSC::ASTBuilder::createBlockStatement): (JSC::ASTBuilder::createIfStatement): (JSC::ASTBuilder::createForLoop): (JSC::ASTBuilder::createForInLoop): (JSC::ASTBuilder::createForOfLoop): (JSC::ASTBuilder::isBindingNode): (JSC::ASTBuilder::createEmptyStatement): (JSC::ASTBuilder::createDeclarationStatement): (JSC::ASTBuilder::createVarStatement): (JSC::ASTBuilder::createLetStatement): (JSC::ASTBuilder::createEmptyVarExpression): (JSC::ASTBuilder::createEmptyLetExpression): (JSC::ASTBuilder::createReturnStatement): (JSC::ASTBuilder::createTryStatement): (JSC::ASTBuilder::createSwitchStatement): (JSC::ASTBuilder::appendStatement): (JSC::ASTBuilder::createCommaExpr): (JSC::ASTBuilder::appendObjectPatternEntry): (JSC::ASTBuilder::createBindingLocation): (JSC::ASTBuilder::setEndOffset): (JSC::ASTBuilder::Scope::Scope): (JSC::ASTBuilder::makeAssignNode): (JSC::ASTBuilder::varDeclarations): Deleted. (JSC::ASTBuilder::addVar): Deleted. * parser/Keywords.table: * parser/NodeConstructors.h: (JSC::ReadModifyResolveNode::ReadModifyResolveNode): (JSC::AssignResolveNode::AssignResolveNode): (JSC::ExprStatementNode::ExprStatementNode): (JSC::DeclarationStatement::DeclarationStatement): (JSC::EmptyVarExpression::EmptyVarExpression): (JSC::EmptyLetExpression::EmptyLetExpression): (JSC::IfElseNode::IfElseNode): (JSC::WhileNode::WhileNode): (JSC::ForNode::ForNode): (JSC::CaseBlockNode::CaseBlockNode): (JSC::SwitchNode::SwitchNode): (JSC::ConstDeclNode::ConstDeclNode): (JSC::BlockNode::BlockNode): (JSC::EnumerationNode::EnumerationNode): (JSC::ForInNode::ForInNode): (JSC::ForOfNode::ForOfNode): (JSC::ObjectPatternNode::create): (JSC::BindingNode::create): (JSC::BindingNode::BindingNode): (JSC::VarStatementNode::VarStatementNode): Deleted. * parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode): (JSC::ScopeNode::singleStatement): (JSC::ProgramNode::ProgramNode): (JSC::EvalNode::EvalNode): (JSC::FunctionNode::FunctionNode): (JSC::FunctionNode::finishParsing): (JSC::VariableEnvironmentNode::VariableEnvironmentNode): * parser/Nodes.h: (JSC::VariableEnvironmentNode::VariableEnvironmentNode): (JSC::VariableEnvironmentNode::lexicalVariables): (JSC::ScopeNode::usesThis): (JSC::ScopeNode::needsActivationForMoreThanVariables): (JSC::ScopeNode::needsActivation): (JSC::ScopeNode::hasCapturedVariables): (JSC::ScopeNode::captures): (JSC::ScopeNode::varDeclarations): (JSC::ScopeNode::functionStack): (JSC::ScopeNode::neededConstants): (JSC::ProgramNode::startColumn): (JSC::ProgramNode::endColumn): (JSC::EvalNode::startColumn): (JSC::EvalNode::endColumn): (JSC::BindingNode::boundProperty): (JSC::BindingNode::divotStart): (JSC::BindingNode::divotEnd): (JSC::ScopeNode::capturedVariableCount): Deleted. (JSC::ScopeNode::capturedVariables): Deleted. (JSC::ScopeNode::varStack): Deleted. There is a new class called 'VariableEnvironmentNode' that has the necessary fields to model a lexical scope. Multiple AST nodes now also inherit from VariableEnvironmentNode. * parser/Parser.cpp: (JSC::Parser<LexerType>::parseInner): (JSC::Parser<LexerType>::didFinishParsing): (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseVariableDeclaration): (JSC::Parser<LexerType>::parseWhileStatement): (JSC::Parser<LexerType>::parseVariableDeclarationList): (JSC::Parser<LexerType>::createBindingPattern): (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression): (JSC::Parser<LexerType>::parseDestructuringPattern): (JSC::Parser<LexerType>::parseConstDeclarationList): (JSC::Parser<LexerType>::parseForStatement): (JSC::Parser<LexerType>::parseBreakStatement): (JSC::Parser<LexerType>::parseContinueStatement): (JSC::Parser<LexerType>::parseSwitchStatement): (JSC::Parser<LexerType>::parseTryStatement): (JSC::Parser<LexerType>::parseBlockStatement): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::parseFunctionInfo): (JSC::Parser<LexerType>::parseClassDeclaration): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExpressionOrLabelStatement): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseGetterSetter): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseVarDeclaration): Deleted. (JSC::Parser<LexerType>::parseVarDeclarationList): Deleted. * parser/Parser.h: (JSC::Scope::Scope): (JSC::Scope::setIsFunction): (JSC::Scope::isFunction): (JSC::Scope::isFunctionBoundary): (JSC::Scope::setIsLexicalScope): (JSC::Scope::isLexicalScope): (JSC::Scope::declaredVariables): (JSC::Scope::finalizeLexicalEnvironment): (JSC::Scope::computeLexicallyCapturedVariablesAndPurgeCandidates): (JSC::Scope::declareCallee): (JSC::Scope::declareVariable): (JSC::Scope::declareLexicalVariable): (JSC::Scope::hasDeclaredVariable): (JSC::Scope::hasLexicallyDeclaredVariable): (JSC::Scope::hasDeclaredParameter): (JSC::Scope::declareWrite): (JSC::Scope::preventAllVariableDeclarations): (JSC::Scope::preventVarDeclarations): (JSC::Scope::allowsVarDeclarations): (JSC::Scope::allowsLexicalDeclarations): (JSC::Scope::declareParameter): (JSC::Scope::declareBoundParameter): (JSC::Scope::useVariable): (JSC::Scope::setNeedsFullActivation): (JSC::Scope::needsFullActivation): (JSC::Scope::hasDirectSuper): (JSC::Scope::setNeedsSuperBinding): (JSC::Scope::collectFreeVariables): (JSC::Scope::getCapturedVars): (JSC::Scope::copyCapturedVariablesToVector): (JSC::Parser::AutoCleanupLexicalScope::AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::~AutoCleanupLexicalScope): (JSC::Parser::AutoCleanupLexicalScope::setIsValid): (JSC::Parser::AutoCleanupLexicalScope::isValid): (JSC::Parser::AutoCleanupLexicalScope::setPopped): (JSC::Parser::AutoCleanupLexicalScope::scope): (JSC::Parser::currentScope): (JSC::Parser::pushScope): (JSC::Parser::popScopeInternal): (JSC::Parser::popScope): (JSC::Parser::declareVariable): (JSC::Parser::hasDeclaredVariable): (JSC::Parser::hasDeclaredParameter): (JSC::Parser::declareWrite): (JSC::Parser::findCachedFunctionInfo): (JSC::Parser::isFunctionBodyNode): (JSC::Parser::continueIsValid): (JSC::Parser::pushLabel): (JSC::Parser::popLabel): (JSC::Parser::getLabel): (JSC::Parser::isLETMaskedAsIDENT): (JSC::Parser<LexerType>::parse): (JSC::Scope::preventNewDecls): Deleted. (JSC::Scope::allowsNewDecls): Deleted. (JSC::Scope::getCapturedVariables): Deleted. There are basic parser changes that now allow for the 'let' keyword. The trickiest change is how we will still treat 'let' as an identifier for sloppy-mode code sometimes. For example, "var let = ..." is allowed but "let let" or "const let" is not. The most significant change to the parser made for this patch is appropriating the Scope struct to also also model a lexical scope. Changes were made in how we track captured variables to account for this. In general, I think some of this code could benefit from a slight refactoring to make things cleaner. * parser/ParserTokens.h: * parser/SyntaxChecker.h: (JSC::SyntaxChecker::createNewExpr): (JSC::SyntaxChecker::createConditionalExpr): (JSC::SyntaxChecker::createAssignResolve): (JSC::SyntaxChecker::createEmptyVarExpression): (JSC::SyntaxChecker::createEmptyLetExpression): (JSC::SyntaxChecker::createClassExpr): (JSC::SyntaxChecker::createClassDeclStatement): (JSC::SyntaxChecker::createBlockStatement): (JSC::SyntaxChecker::createExprStatement): (JSC::SyntaxChecker::createIfStatement): (JSC::SyntaxChecker::createForLoop): (JSC::SyntaxChecker::createForInLoop): (JSC::SyntaxChecker::createForOfLoop): (JSC::SyntaxChecker::createEmptyStatement): (JSC::SyntaxChecker::createVarStatement): (JSC::SyntaxChecker::createLetStatement): (JSC::SyntaxChecker::createReturnStatement): (JSC::SyntaxChecker::createBreakStatement): (JSC::SyntaxChecker::createContinueStatement): (JSC::SyntaxChecker::createTryStatement): (JSC::SyntaxChecker::createSwitchStatement): (JSC::SyntaxChecker::createWhileStatement): (JSC::SyntaxChecker::createWithStatement): (JSC::SyntaxChecker::createDoWhileStatement): (JSC::SyntaxChecker::createGetterOrSetterProperty): (JSC::SyntaxChecker::appendStatement): (JSC::SyntaxChecker::combineCommaNodes): (JSC::SyntaxChecker::evalCount): (JSC::SyntaxChecker::appendBinaryExpressionInfo): (JSC::SyntaxChecker::operatorStackPop): (JSC::SyntaxChecker::addVar): Deleted. * parser/VariableEnvironment.cpp: Added. (JSC::VariableEnvironment::markVariableAsCapturedIfDefined): (JSC::VariableEnvironment::markVariableAsCaptured): (JSC::VariableEnvironment::markAllVariablesAsCaptured): (JSC::VariableEnvironment::hasCapturedVariables): (JSC::VariableEnvironment::captures): (JSC::VariableEnvironment::swap): * parser/VariableEnvironment.h: Added. (JSC::VariableEnvironmentEntry::isCaptured): (JSC::VariableEnvironmentEntry::isConstant): (JSC::VariableEnvironmentEntry::isVar): (JSC::VariableEnvironmentEntry::isLet): (JSC::VariableEnvironmentEntry::setIsCaptured): (JSC::VariableEnvironmentEntry::setIsConstant): (JSC::VariableEnvironmentEntry::setIsVar): (JSC::VariableEnvironmentEntry::setIsLet): (JSC::VariableEnvironmentEntry::clearIsVar): (JSC::VariableEnvironment::begin): (JSC::VariableEnvironment::end): (JSC::VariableEnvironment::add): (JSC::VariableEnvironment::size): (JSC::VariableEnvironment::contains): (JSC::VariableEnvironment::remove): VariableEnvironment is a new class that keeps track of the static environment in the parser and the bytecode generator. VariableEnvironment behaves like SymbolTable but for the bytecode generator. It keeps track of variable types, i.e, if a variable is a "var", "let", "const" and whether or not its captured. * runtime/CodeCache.cpp: (JSC::CodeCache::getGlobalCodeBlock): (JSC::CodeCache::getProgramCodeBlock): (JSC::CodeCache::getEvalCodeBlock): (JSC::CodeCache::getFunctionExecutableFromGlobalCode): * runtime/CodeCache.h: (JSC::CodeCache::clear): * runtime/CommonSlowPaths.cpp: (JSC::SLOW_PATH_DECL): * runtime/CommonSlowPaths.h: * runtime/ExceptionHelpers.cpp: (JSC::createErrorForInvalidGlobalAssignment): (JSC::createTDZError): (JSC::throwOutOfMemoryError): * runtime/ExceptionHelpers.h: * runtime/Executable.cpp: (JSC::EvalExecutable::create): (JSC::ProgramExecutable::initializeGlobalProperties): * runtime/Executable.h: * runtime/JSCJSValue.h: (JSC::jsUndefined): (JSC::jsTDZValue): (JSC::jsBoolean): * runtime/JSEnvironmentRecord.h: (JSC::JSEnvironmentRecord::finishCreationUninitialized): (JSC::JSEnvironmentRecord::finishCreation): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::createProgramCodeBlock): (JSC::JSGlobalObject::createEvalCodeBlock): * runtime/JSGlobalObject.h: (JSC::JSGlobalObject::weakRandomInteger): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): * runtime/JSLexicalEnvironment.cpp: (JSC::JSLexicalEnvironment::symbolTableGet): * runtime/JSLexicalEnvironment.h: (JSC::JSLexicalEnvironment::create): * runtime/JSScope.cpp: (JSC::JSScope::resolve): (JSC::JSScope::abstractResolve): (JSC::JSScope::collectVariablesUnderTDZ): (JSC::JSScope::isLexicalScope): (JSC::resolveModeName): * runtime/JSScope.h: * runtime/PropertySlot.h: (JSC::PropertySlot::setValue): * runtime/SymbolTable.cpp: (JSC::SymbolTable::SymbolTable): (JSC::SymbolTable::cloneScopePart): * runtime/SymbolTable.h: SymbolTable now uses an extra bit to know if it corresponds to a "let"-like environment or not. * runtime/WriteBarrier.h: (JSC::WriteBarrierBase<Unknown>::get): (JSC::WriteBarrierBase<Unknown>::clear): (JSC::WriteBarrierBase<Unknown>::setUndefined): (JSC::WriteBarrierBase<Unknown>::setStartingValue): (JSC::WriteBarrierBase<Unknown>::isNumber): (JSC::WriteBarrierBase<Unknown>::isObject): (JSC::WriteBarrierBase<Unknown>::isNull): * tests/stress/activation-sink-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): (foo.cap): * tests/stress/activation-sink-osrexit-default-value-tdz-error.js: Added. (shouldThrowTDZ): (bar): * tests/stress/lexical-let-and-with-statement.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-exception-handling.js: Added. (truth): (assert): (.): * tests/stress/lexical-let-global-not-captured-variables.js: Added. (truth): (assert): (foo): (.let.capY): * tests/stress/lexical-let-loop-semantics.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-not-strict-mode.js: Added. (truth): (assert): (shouldThrowTDZ): (.): * tests/stress/lexical-let-semantics.js: Added. (truth): (assert): (let.globalFunction): (let.retGlobalNumberCaptured): (let.setGlobalNumberCaptured): (.): * tests/stress/lexical-let-tdz.js: Added. (truth): (assert): (shouldThrowTDZ): (.): LayoutTests: * js/dom/reserved-words-as-property-expected.txt: * js/keywords-and-reserved_words-expected.txt: * js/let-syntax-expected.txt: Added. * js/let-syntax.html: Added. * js/reserved-words-strict-expected.txt: * js/script-tests/keywords-and-reserved_words.js: * js/script-tests/let-syntax.js: Added. (truth): (assert): (hasSyntaxError): (shouldHaveSyntaxError): (shouldNotHaveSyntaxError): (shouldHaveSyntaxErrorStrictOnly): * js/script-tests/reserved-words-strict.js: * js/script-tests/statement-list-item-syntax-errors.js: (testSyntax): (runTests): * js/statement-list-item-syntax-errors-expected.txt: Canonical link: https://commits.webkit.org/165034@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2015-07-15 21:41:08 +00:00
PASS successfullyParsed is true
TEST COMPLETE