[ES6] implement block scoping to enable 'let'
authorsaambarati1@gmail.com <saambarati1@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Jul 2015 21:41:08 +0000 (21:41 +0000)
committersaambarati1@gmail.com <saambarati1@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Jul 2015 21:41:08 +0000 (21:41 +0000)
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:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@186860 268f45cc-cd09-0410-ab3c-d52691b4dbfc

95 files changed:
LayoutTests/ChangeLog
LayoutTests/js/dom/reserved-words-as-property-expected.txt
LayoutTests/js/keywords-and-reserved_words-expected.txt
LayoutTests/js/let-syntax-expected.txt [new file with mode: 0644]
LayoutTests/js/let-syntax.html [new file with mode: 0644]
LayoutTests/js/reserved-words-strict-expected.txt
LayoutTests/js/script-tests/keywords-and-reserved_words.js
LayoutTests/js/script-tests/let-syntax.js [new file with mode: 0644]
LayoutTests/js/script-tests/reserved-words-strict.js
LayoutTests/js/script-tests/statement-list-item-syntax-errors.js
LayoutTests/js/statement-list-item-syntax-errors-expected.txt
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/builtins/BuiltinExecutables.cpp
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/EvalCodeCache.h
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
Source/JavaScriptCore/debugger/DebuggerScope.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/ftl/FTLOperations.cpp
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/jit/CCallHelpers.h
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITInlines.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.h
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/Keywords.table
Source/JavaScriptCore/parser/NodeConstructors.h
Source/JavaScriptCore/parser/Nodes.cpp
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/parser/ParserTokens.h
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/parser/VariableEnvironment.cpp [new file with mode: 0644]
Source/JavaScriptCore/parser/VariableEnvironment.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/CodeCache.cpp
Source/JavaScriptCore/runtime/CodeCache.h
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.h
Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
Source/JavaScriptCore/runtime/ExceptionHelpers.h
Source/JavaScriptCore/runtime/Executable.cpp
Source/JavaScriptCore/runtime/Executable.h
Source/JavaScriptCore/runtime/JSCJSValue.h
Source/JavaScriptCore/runtime/JSEnvironmentRecord.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSLexicalEnvironment.cpp
Source/JavaScriptCore/runtime/JSLexicalEnvironment.h
Source/JavaScriptCore/runtime/JSScope.cpp
Source/JavaScriptCore/runtime/JSScope.h
Source/JavaScriptCore/runtime/PropertySlot.h
Source/JavaScriptCore/runtime/SymbolTable.cpp
Source/JavaScriptCore/runtime/SymbolTable.h
Source/JavaScriptCore/runtime/WriteBarrier.h
Source/JavaScriptCore/tests/stress/activation-sink-default-value-tdz-error.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/activation-sink-osrexit-default-value-tdz-error.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/lexical-let-and-with-statement.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/lexical-let-exception-handling.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/lexical-let-global-not-captured-variables.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/lexical-let-loop-semantics.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/lexical-let-not-strict-mode.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/lexical-let-semantics.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/lexical-let-tdz.js [new file with mode: 0644]

index fe38124..1f5b560 100644 (file)
@@ -1,3 +1,29 @@
+2015-07-15  Saam barati  <saambarati1@gmail.com>
+
+        [ES6] implement block scoping to enable 'let'
+        https://bugs.webkit.org/show_bug.cgi?id=142944
+
+        Reviewed by Filip Pizlo.
+
+        * 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:
+
 2015-07-15  Tim Horton  <timothy_horton@apple.com>
 
         Factor rect shrink-wrapping code out of RenderThemeMac for future reuse
index 6d69c9b..890de19 100644 (file)
@@ -1353,18 +1353,18 @@ PASS ({ let: 42 }.let === 42) is true
 PASS (function(){({ let: 42 }.let === 42)}); true is true
 PASS ({ get let(){}, set let(x){}, parsedOkay: 42 }.parsedOkay === 42) is true
 PASS (function(){({ get let(){}, set let(x){}, parsedOkay: 42 }.parsedOkay === 42)}); true is true
-PASS "use strict";var let; true threw exception SyntaxError: Cannot use the reserved word 'let' as a variable name in strict mode..
-PASS (function(){"use strict";var let; true}); true threw exception SyntaxError: Cannot use the reserved word 'let' as a variable name in strict mode..
-PASS "use strict";var let = 42; let === 42 threw exception SyntaxError: Cannot use the reserved word 'let' as a variable name in strict mode..
-PASS (function(){"use strict";var let = 42; let === 42}); true threw exception SyntaxError: Cannot use the reserved word 'let' as a variable name in strict mode..
-PASS "use strict";function g(let){ "use strict"; }; true threw exception SyntaxError: Cannot use the reserved word 'let' as a variable name in strict mode..
-PASS (function(){"use strict";function g(let){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the reserved word 'let' as a variable name in strict mode..
-PASS "use strict";/let/.test(function g(let){ "use strict"; }) threw exception SyntaxError: Cannot use the reserved word 'let' as a variable name in strict mode..
-PASS (function(){"use strict";/let/.test(function g(let){ "use strict"; })}); true threw exception SyntaxError: Cannot use the reserved word 'let' as a variable name in strict mode..
-PASS "use strict";try{}catch(let){}; true threw exception SyntaxError: Cannot use the reserved word 'let' as a catch variable name in strict mode..
-PASS (function(){"use strict";try{}catch(let){}; true}); true threw exception SyntaxError: Cannot use the reserved word 'let' as a catch variable name in strict mode..
-PASS "use strict";function let(){ "use strict"; }; true threw exception SyntaxError: Cannot use the reserved word 'let' as a function name in strict mode..
-PASS (function(){"use strict";function let(){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the reserved word 'let' as a function name in strict mode..
+PASS "use strict";var let; true threw exception SyntaxError: Cannot use the keyword 'let' as a variable name..
+PASS (function(){"use strict";var let; true}); true threw exception SyntaxError: Cannot use the keyword 'let' as a variable name..
+PASS "use strict";var let = 42; let === 42 threw exception SyntaxError: Cannot use the keyword 'let' as a variable name..
+PASS (function(){"use strict";var let = 42; let === 42}); true threw exception SyntaxError: Cannot use the keyword 'let' as a variable name..
+PASS "use strict";function g(let){ "use strict"; }; true threw exception SyntaxError: Cannot use the keyword 'let' as a variable name..
+PASS (function(){"use strict";function g(let){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the keyword 'let' as a variable name..
+PASS "use strict";/let/.test(function g(let){ "use strict"; }) threw exception SyntaxError: Cannot use the keyword 'let' as a variable name..
+PASS (function(){"use strict";/let/.test(function g(let){ "use strict"; })}); true threw exception SyntaxError: Cannot use the keyword 'let' as a variable name..
+PASS "use strict";try{}catch(let){}; true threw exception SyntaxError: Cannot use the keyword 'let' as a catch variable name..
+PASS (function(){"use strict";try{}catch(let){}; true}); true threw exception SyntaxError: Cannot use the keyword 'let' as a catch variable name..
+PASS "use strict";function let(){ "use strict"; }; true threw exception SyntaxError: Cannot use the keyword 'let' as a function name..
+PASS (function(){"use strict";function let(){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the keyword 'let' as a function name..
 PASS "use strict";({ "let": 42 }.let === 42) is true
 PASS (function(){"use strict";({ "let": 42 }.let === 42)}); true is true
 PASS "use strict";({ let: 42 }.let === 42) is true
index 59663fe..c58c77d 100644 (file)
@@ -60,13 +60,14 @@ PASS classifyIdentifier("import") is "keyword"
 PASS classifyIdentifier("super") is "keyword"
 PASS classifyIdentifier("implements") is "strict"
 PASS classifyIdentifier("interface") is "strict"
-PASS classifyIdentifier("let") is "strict"
 PASS classifyIdentifier("package") is "strict"
 PASS classifyIdentifier("private") is "strict"
 PASS classifyIdentifier("protected") is "strict"
 PASS classifyIdentifier("public") is "strict"
 PASS classifyIdentifier("static") is "strict"
 PASS classifyIdentifier("yield") is "strict"
+PASS isKeyword("let") is false
+PASS isStrictKeyword("let") is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/LayoutTests/js/let-syntax-expected.txt b/LayoutTests/js/let-syntax-expected.txt
new file mode 100644 (file)
index 0000000..cc13851
--- /dev/null
@@ -0,0 +1,123 @@
+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 Has syntax error: 'let let;'
+PASS Has syntax error: ''use strict'; let let;'
+PASS Has syntax error: 'const let;'
+PASS Has syntax error: ''use strict'; const let;'
+PASS Has syntax error: 'let {let};'
+PASS Has syntax error: ''use strict'; let {let};'
+PASS Has syntax error: 'let {l: let};'
+PASS Has syntax error: ''use strict'; let {l: let};'
+PASS Has syntax error: 'let {l: {let}};'
+PASS Has syntax error: ''use strict'; let {l: {let}};'
+PASS Has syntax error: 'let {l: [let]};'
+PASS Has syntax error: ''use strict'; let {l: [let]};'
+PASS Has syntax error: 'var {let};'
+PASS Has syntax error: ''use strict'; var {let};'
+PASS Has syntax error: 'let x, x;'
+PASS Has syntax error: ''use strict'; let x, x;'
+PASS Has syntax error: 'let x = 20, y, x = 40;'
+PASS Has syntax error: ''use strict'; let x = 20, y, x = 40;'
+PASS Has syntax error: 'let x = 20, y; let x = 40;'
+PASS Has syntax error: ''use strict'; let x = 20, y; let x = 40;'
+PASS Has syntax error: 'let x = 20, y, {x} = {};'
+PASS Has syntax error: ''use strict'; let x = 20, y, {x} = {};'
+PASS Has syntax error: 'let x = 20, y; let {x} = {};'
+PASS Has syntax error: ''use strict'; let x = 20, y; let {x} = {};'
+PASS Has syntax error: 'let {x, y, z, x} = {};'
+PASS Has syntax error: ''use strict'; let {x, y, z, x} = {};'
+PASS Has syntax error: 'let {x: xx, y, x: xx} = {};'
+PASS Has syntax error: ''use strict'; let {x: xx, y, x: xx} = {};'
+PASS Has syntax error: 'let {x: xx,  foo: [xx]} = {foo:[12]};'
+PASS Has syntax error: ''use strict'; let {x: xx,  foo: [xx]} = {foo:[12]};'
+PASS Has syntax error: 'let {x: xx,  foo: {y: xx}} = {foo:[12]};'
+PASS Has syntax error: ''use strict'; let {x: xx,  foo: {y: xx}} = {foo:[12]};'
+PASS Has syntax error: 'for (let; ; ) {}'
+PASS Has syntax error: ''use strict'; for (let; ; ) {}'
+PASS Has syntax error: 'let arr = []; for (let    of arr) {}'
+PASS Has syntax error: ''use strict'; let arr = []; for (let    of arr) {}'
+PASS Has syntax error: 'let obj = {}; for (let    in arr) {}'
+PASS Has syntax error: ''use strict'; let obj = {}; for (let    in arr) {}'
+PASS Has syntax error: 'for (let i = 20, j = 40, i = 10; i < 10; i++) {}'
+PASS Has syntax error: ''use strict'; for (let i = 20, j = 40, i = 10; i < 10; i++) {}'
+PASS Has syntax error: 'let x = 20; if (truth()) let x = 40;'
+PASS Has syntax error: ''use strict'; let x = 20; if (truth()) let x = 40;'
+PASS Has syntax error: 'let baz = 20; if (truth()) { let x = 20; let x = 40;} '
+PASS Has syntax error: ''use strict'; let baz = 20; if (truth()) { let x = 20; let x = 40;} '
+PASS Has syntax error: 'function foo() { var bar; let bar; }'
+PASS Has syntax error: ''use strict'; function foo() { var bar; let bar; }'
+PASS Has syntax error: 'function foo(bar) { let bar; }'
+PASS Has syntax error: ''use strict'; function foo(bar) { let bar; }'
+PASS Has syntax error: 'function foo() {}; let foo;'
+PASS Has syntax error: ''use strict'; function foo() {}; let foo;'
+PASS Has syntax error: 'function foo() {}; function bar(){} let baz, {bar} = {};'
+PASS Has syntax error: ''use strict'; function foo() {}; function bar(){} let baz, {bar} = {};'
+PASS Has syntax error: 'function foo() {}; function bar(){} let baz, {f: {bar}} = {f:{}};'
+PASS Has syntax error: ''use strict'; function foo() {}; function bar(){} let baz, {f: {bar}} = {f:{}};'
+PASS Has syntax error: 'function foo() {}; function bar(){} let baz, {f: [bar]} = {f:[10]};'
+PASS Has syntax error: ''use strict'; function foo() {}; function bar(){} let baz, {f: [bar]} = {f:[10]};'
+PASS Has syntax error: 'for (let let = 0; let < 10; let++) {}'
+PASS Has syntax error: ''use strict'; for (let let = 0; let < 10; let++) {}'
+PASS Has syntax error: 'for (let of []) {}'
+PASS Has syntax error: ''use strict'; for (let of []) {}'
+PASS Has syntax error: 'for (let in {}) {}'
+PASS Has syntax error: ''use strict'; for (let in {}) {}'
+PASS Does not have syntax error: 'let;'
+PASS Has syntax error: ''use strict'; let;'
+PASS Does not have syntax error: 'var let;'
+PASS Has syntax error: ''use strict'; var let;'
+PASS Does not have syntax error: 'var {let} = 40;'
+PASS Has syntax error: ''use strict'; var {let} = 40;'
+PASS Does not have syntax error: 'var [let] = 40;'
+PASS Has syntax error: ''use strict'; var [let] = 40;'
+PASS Does not have syntax error: 'var {p: let} = 40;'
+PASS Has syntax error: ''use strict'; var {p: let} = 40;'
+PASS Does not have syntax error: '(function test(let){});'
+PASS Has syntax error: ''use strict'; (function test(let){});'
+PASS Does not have syntax error: 'let: for (v of []) break let;'
+PASS Has syntax error: ''use strict'; let: for (v of []) break let;'
+PASS Does not have syntax error: 'let: for (v of []) continue let;'
+PASS Has syntax error: ''use strict'; let: for (v of []) continue let;'
+PASS Does not have syntax error: 'let: for (v in {}) break;'
+PASS Has syntax error: ''use strict'; let: for (v in {}) break;'
+PASS Does not have syntax error: 'let: for (v in {}) break;'
+PASS Has syntax error: ''use strict'; let: for (v in {}) break;'
+PASS Does not have syntax error: 'let: for (var v = 0; false; ) {};'
+PASS Has syntax error: ''use strict'; let: for (var v = 0; false; ) {};'
+PASS Does not have syntax error: 'try { } catch(let) {}'
+PASS Has syntax error: ''use strict'; try { } catch(let) {}'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/let-syntax.html b/LayoutTests/js/let-syntax.html
new file mode 100644 (file)
index 0000000..2a6ad9b
--- /dev/null
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../resources/js-test-pre.js"></script>
+<script src="script-tests/let-syntax.js"></script>
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
index 40db19a..fa017ff 100644 (file)
@@ -6,7 +6,6 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 SHOULD BE RESERVED:
 PASS isReserved('implements') is true
 PASS isReserved('interface') is true
-PASS isReserved('let') is true
 PASS isReserved('package') is true
 PASS isReserved('private') is true
 PASS isReserved('protected') is true
index 4d0194e..22a8685 100644 (file)
@@ -104,10 +104,15 @@ shouldBe('classifyIdentifier("super")', '"keyword"');
 // Future Reserved Words, in strict mode only.
 shouldBe('classifyIdentifier("implements")', '"strict"');
 shouldBe('classifyIdentifier("interface")', '"strict"');
-shouldBe('classifyIdentifier("let")', '"strict"');
 shouldBe('classifyIdentifier("package")', '"strict"');
 shouldBe('classifyIdentifier("private")', '"strict"');
 shouldBe('classifyIdentifier("protected")', '"strict"');
 shouldBe('classifyIdentifier("public")', '"strict"');
 shouldBe('classifyIdentifier("static")', '"strict"');
 shouldBe('classifyIdentifier("yield")', '"strict"');
+
+// This is in a class of its own because it's treated as a keyword
+// in strict-mode and not a keyword in sloppy mode
+// (non-keyword in sloppy mode is temporary).
+shouldBeFalse('isKeyword("let")'); // "var let" is allowed but "let let" is not.
+shouldBeTrue('isStrictKeyword("let")');
diff --git a/LayoutTests/js/script-tests/let-syntax.js b/LayoutTests/js/script-tests/let-syntax.js
new file mode 100644 (file)
index 0000000..f0671de
--- /dev/null
@@ -0,0 +1,114 @@
+description('Tests for ES6 "let"');
+
+function truth() { return true; }
+noInline(truth);
+
+function assert(cond) {
+    if (!cond)
+        throw new Error("Broke assertion");
+}
+
+function hasSyntaxError(str) {
+    let hadError = false;
+    try {
+        eval(str);
+    } catch(e) {
+        if (e.name === "SyntaxError") {
+            hadError = true;
+        }
+    }
+    return hadError; 
+}
+
+function shouldHaveSyntaxError(str) {
+    assert(hasSyntaxError(str));
+    assert(hasSyntaxError("function dummy() { " + str + " }"));
+    testPassed("Has syntax error: '" + str + "'");
+    str = "'use strict'; " + str;
+    assert(hasSyntaxError(str));
+    assert(hasSyntaxError("function dummy() { " + str + " }"));
+    testPassed("Has syntax error: '" + str + "'");
+}
+
+
+function shouldNotHaveSyntaxError(str) {
+    assert(!hasSyntaxError(str));
+    assert(!hasSyntaxError("(function dummy() { " + str + " })"));
+    testPassed("Does not have syntax error: '" + str + "'");
+    str = "'use strict'; " + str;
+    assert(!hasSyntaxError(str));
+    assert(!hasSyntaxError("(function dummy() { " + str + " })"));
+    testPassed("Does not have syntax error: '" + str + "'");
+}
+
+function shouldHaveSyntaxErrorStrictOnly(str)
+{
+    assert(!hasSyntaxError(str));
+    assert(!hasSyntaxError("(function dummy() { " + str + " })"));
+    testPassed("Does not have syntax error: '" + str + "'");
+    str = "'use strict'; " + str;
+    assert(hasSyntaxError(str));
+    assert(hasSyntaxError("(function dummy() { " + str + " })"));
+    testPassed("Has syntax error: '" + str + "'");
+}
+
+shouldNotHaveSyntaxError("let x = 20; if (truth()) { let x = 30; }");
+shouldNotHaveSyntaxError("let {x} = {x:20}; if (truth()) { let {x} = {x : 20}; }");
+shouldNotHaveSyntaxError("let {x} = {x:20}; if (truth()) { let {y: x} = {y : 20}; }");
+shouldNotHaveSyntaxError("let {x, y: [arr]} = {x:20, y: [10]}; if (truth()) { let {y: x} = {y : 20}; }");
+shouldNotHaveSyntaxError("let i = 40; for (let i = 1; i < 2; i++) { let i = 40; i; }");
+shouldNotHaveSyntaxError("let i = 40; let obj = {}; for (let i in obj) { let i = 40; let obj = {}; i; }");
+shouldNotHaveSyntaxError("let i = 40; let obj = []; for (let i of obj) { let i = 40; let obj = {}; i; }");
+shouldNotHaveSyntaxError("let {i} = 20; let obj = []; for (let {i} of obj) { let i = 40; let obj = {}; i; }");
+shouldNotHaveSyntaxError("let {i} = 20; let obj = []; for (let {i} in obj) { let i = 40; let obj = {}; i; }");
+shouldNotHaveSyntaxError("let {i} = 20; let obj = []; for (let {i} = {i: 0}; i < 2; i++) { let i = 40; let obj = {}; i; }");
+shouldNotHaveSyntaxError("function foo() { let foo = 20; }");
+shouldNotHaveSyntaxError("function foo(bar) { if (truth()) { let bar; } }");
+shouldNotHaveSyntaxError("function foo() { var bar; if (truth()) { let bar; } }");
+shouldNotHaveSyntaxError(";({ get let() { return 50; }, set let(x) { return 50;} });");
+
+shouldHaveSyntaxError("let let;");
+shouldHaveSyntaxError("const let;");
+shouldHaveSyntaxError("let {let};");
+shouldHaveSyntaxError("let {l: let};");
+shouldHaveSyntaxError("let {l: {let}};");
+shouldHaveSyntaxError("let {l: [let]};");
+shouldHaveSyntaxError("var {let};");
+shouldHaveSyntaxError("let x, x;");
+shouldHaveSyntaxError("let x = 20, y, x = 40;");
+shouldHaveSyntaxError("let x = 20, y; let x = 40;");
+shouldHaveSyntaxError("let x = 20, y, {x} = {};");
+shouldHaveSyntaxError("let x = 20, y; let {x} = {};");
+shouldHaveSyntaxError("let {x, y, z, x} = {};");
+shouldHaveSyntaxError("let {x: xx, y, x: xx} = {};");
+shouldHaveSyntaxError("let {x: xx,  foo: [xx]} = {foo:[12]};");
+shouldHaveSyntaxError("let {x: xx,  foo: {y: xx}} = {foo:[12]};");
+shouldHaveSyntaxError("for (let; ; ) {}");
+shouldHaveSyntaxError("let arr = []; for (let    of arr) {}");
+shouldHaveSyntaxError("let obj = {}; for (let    in arr) {}");
+shouldHaveSyntaxError("for (let i = 20, j = 40, i = 10; i < 10; i++) {}");
+shouldHaveSyntaxError("let x = 20; if (truth()) let x = 40;");
+shouldHaveSyntaxError("let baz = 20; if (truth()) { let x = 20; let x = 40;} ");
+shouldHaveSyntaxError("function foo() { var bar; let bar; }");
+shouldHaveSyntaxError("function foo(bar) { let bar; }");
+shouldHaveSyntaxError("function foo() {}; let foo;");
+shouldHaveSyntaxError("function foo() {}; function bar(){} let baz, {bar} = {};");
+shouldHaveSyntaxError("function foo() {}; function bar(){} let baz, {f: {bar}} = {f:{}};");
+shouldHaveSyntaxError("function foo() {}; function bar(){} let baz, {f: [bar]} = {f:[10]};");
+shouldHaveSyntaxError("for (let let = 0; let < 10; let++) {}");
+shouldHaveSyntaxError("for (let of []) {}");
+shouldHaveSyntaxError("for (let in {}) {}");
+
+// Stay classy, ES6.
+shouldHaveSyntaxErrorStrictOnly("let;");
+shouldHaveSyntaxErrorStrictOnly("var let;");
+shouldHaveSyntaxErrorStrictOnly("var {let} = 40;");
+shouldHaveSyntaxErrorStrictOnly("var [let] = 40;");
+shouldHaveSyntaxErrorStrictOnly("var {p: let} = 40;");
+shouldHaveSyntaxErrorStrictOnly("(function test(let){});");
+shouldHaveSyntaxErrorStrictOnly("let: for (v of []) break let;");
+shouldHaveSyntaxErrorStrictOnly("let: for (v of []) continue let;");
+shouldHaveSyntaxErrorStrictOnly("let: for (v in {}) break;");
+shouldHaveSyntaxErrorStrictOnly("let: for (v in {}) break;");
+shouldHaveSyntaxErrorStrictOnly("let: for (var v = 0; false; ) {};");
+shouldHaveSyntaxErrorStrictOnly("try { } catch(let) {}");
index de5d062..18b8a21 100644 (file)
@@ -16,7 +16,6 @@ function isReserved(word)
 
 var reservedWords = [
     "implements",
-    "let",
     "private",
     "public",
     "yield",
index 1278a31..415a63f 100644 (file)
@@ -43,13 +43,13 @@ function testSyntax(asBlock, body, throwStatus) {
 }
 
 function runTests() {
-    // FIXME: This needs to consider 'let' in bug:
-    // https://bugs.webkit.org/show_bug.cgi?id=142944
     var bodies = [
         "class C {};",
         "class C extends (class A {}) {};",
         "const c = 40;",
-        "const c = 40, d = 50;"
+        "const c = 40, d = 50;",
+        "let c = 40;",
+        "let c = 40, d = 50;"
     ];
     for (var body of bodies) {
         testSyntax(true, body, NoneShouldThrow);
index c000dfd..0461750 100644 (file)
@@ -99,6 +99,54 @@ PASS Had syntax error 'if (i = 0)  const c = 40, d = 50;  else  const c = 40, d
 PASS Had syntax error 'if (i = 0) i++; else  const c = 40, d = 50; '
 PASS Had syntax error 'if (i = 0)  const c = 40, d = 50;  else i++;'
 PASS Had syntax error 'with ({})  const c = 40, d = 50; '
+PASS Did not have syntax error: 'for (var i = 0; i < 4; i++) { let c = 40; }'
+PASS Did not have syntax error: 'for (var i in {}) { let c = 40; }'
+PASS Did not have syntax error: 'for (i in {}) { let c = 40; }'
+PASS Did not have syntax error: 'for (var i of []) { let c = 40; }'
+PASS Did not have syntax error: 'for (i of []) { let c = 40; }'
+PASS Did not have syntax error: 'while (i = 0) { let c = 40; }'
+PASS Did not have syntax error: 'do { let c = 40; } while (i = 0)'
+PASS Did not have syntax error: 'if (i = 0) { let c = 40; }'
+PASS Did not have syntax error: 'if (i = 0) { let c = 40; } else { let c = 40; }'
+PASS Did not have syntax error: 'if (i = 0) i++; else { let c = 40; }'
+PASS Did not have syntax error: 'if (i = 0) { let c = 40; } else i++;'
+PASS Did not have syntax error: 'with ({}) { let c = 40; }'
+PASS Had syntax error 'for (var i = 0; i < 4; i++)  let c = 40; '
+PASS Had syntax error 'for (var i in {})  let c = 40; '
+PASS Had syntax error 'for (i in {})  let c = 40; '
+PASS Had syntax error 'for (var i of [])  let c = 40; '
+PASS Had syntax error 'for (i of [])  let c = 40; '
+PASS Had syntax error 'while (i = 0)  let c = 40; '
+PASS Had syntax error 'do  let c = 40;  while (i = 0)'
+PASS Had syntax error 'if (i = 0)  let c = 40; '
+PASS Had syntax error 'if (i = 0)  let c = 40;  else  let c = 40; '
+PASS Had syntax error 'if (i = 0) i++; else  let c = 40; '
+PASS Had syntax error 'if (i = 0)  let c = 40;  else i++;'
+PASS Had syntax error 'with ({})  let c = 40; '
+PASS Did not have syntax error: 'for (var i = 0; i < 4; i++) { let c = 40, d = 50; }'
+PASS Did not have syntax error: 'for (var i in {}) { let c = 40, d = 50; }'
+PASS Did not have syntax error: 'for (i in {}) { let c = 40, d = 50; }'
+PASS Did not have syntax error: 'for (var i of []) { let c = 40, d = 50; }'
+PASS Did not have syntax error: 'for (i of []) { let c = 40, d = 50; }'
+PASS Did not have syntax error: 'while (i = 0) { let c = 40, d = 50; }'
+PASS Did not have syntax error: 'do { let c = 40, d = 50; } while (i = 0)'
+PASS Did not have syntax error: 'if (i = 0) { let c = 40, d = 50; }'
+PASS Did not have syntax error: 'if (i = 0) { let c = 40, d = 50; } else { let c = 40, d = 50; }'
+PASS Did not have syntax error: 'if (i = 0) i++; else { let c = 40, d = 50; }'
+PASS Did not have syntax error: 'if (i = 0) { let c = 40, d = 50; } else i++;'
+PASS Did not have syntax error: 'with ({}) { let c = 40, d = 50; }'
+PASS Had syntax error 'for (var i = 0; i < 4; i++)  let c = 40, d = 50; '
+PASS Had syntax error 'for (var i in {})  let c = 40, d = 50; '
+PASS Had syntax error 'for (i in {})  let c = 40, d = 50; '
+PASS Had syntax error 'for (var i of [])  let c = 40, d = 50; '
+PASS Had syntax error 'for (i of [])  let c = 40, d = 50; '
+PASS Had syntax error 'while (i = 0)  let c = 40, d = 50; '
+PASS Had syntax error 'do  let c = 40, d = 50;  while (i = 0)'
+PASS Had syntax error 'if (i = 0)  let c = 40, d = 50; '
+PASS Had syntax error 'if (i = 0)  let c = 40, d = 50;  else  let c = 40, d = 50; '
+PASS Had syntax error 'if (i = 0) i++; else  let c = 40, d = 50; '
+PASS Had syntax error 'if (i = 0)  let c = 40, d = 50;  else i++;'
+PASS Had syntax error 'with ({})  let c = 40, d = 50; '
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 8ae8665..8ff2bb9 100644 (file)
@@ -385,6 +385,7 @@ set(JavaScriptCore_SOURCES
     parser/SourceCode.cpp
     parser/SourceProvider.cpp
     parser/SourceProviderCache.cpp
+    parser/VariableEnvironment.cpp
 
     profiler/LegacyProfiler.cpp
     profiler/Profile.cpp
index 7b16be8..77b1f5d 100644 (file)
@@ -1,3 +1,599 @@
+2015-07-15  Saam barati  <saambarati1@gmail.com>
+
+        [ES6] implement block scoping to enable 'let'
+        https://bugs.webkit.org/show_bug.cgi?id=142944
+
+        Reviewed by Filip Pizlo.
+
+        * 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):
+        (.):
+
 2015-07-15  Anders Carlsson  <andersca@apple.com>
 
         Make JavaScriptCore SPI headers used by WebCore SPI headers self-contained
index 1c1e5f3..fc28f3b 100644 (file)
     <ClCompile Include="..\parser\SourceCode.cpp" />
     <ClCompile Include="..\parser\SourceProvider.cpp" />
     <ClCompile Include="..\parser\SourceProviderCache.cpp" />
+    <ClCompile Include="..\parser\VariableEnvironment.cpp" />
     <ClCompile Include="..\profiler\LegacyProfiler.cpp" />
     <ClCompile Include="..\profiler\Profile.cpp" />
     <ClCompile Include="..\profiler\ProfileGenerator.cpp" />
     <ClInclude Include="..\parser\SourceProviderCache.h" />
     <ClInclude Include="..\parser\SourceProviderCacheItem.h" />
     <ClInclude Include="..\parser\SyntaxChecker.h" />
+    <ClInclude Include="..\parser\VariableEnvironment.h" />
     <ClInclude Include="..\profiler\CallIdentifier.h" />
     <ClInclude Include="..\profiler\LegacyProfiler.h" />
     <ClInclude Include="..\profiler\Profile.h" />
index a66b830..aaf3256 100644 (file)
     <ClCompile Include="..\parser\SourceProviderCache.cpp">
       <Filter>parser</Filter>
     </ClCompile>
+    <ClCompile Include="..\parser\VariableEnvironment.cpp">
+      <Filter>parser</Filter>
+    </ClCompile>
     <ClCompile Include="..\profiler\LegacyProfiler.cpp">
       <Filter>profiler</Filter>
     </ClCompile>
     <ClInclude Include="..\parser\SyntaxChecker.h">
       <Filter>parser</Filter>
     </ClInclude>
+    <ClInclude Include="..\parser\VariableEnvironment.h">
+      <Filter>parser</Filter>
+    </ClInclude>
     <ClInclude Include="..\profiler\CallIdentifier.h">
       <Filter>profiler</Filter>
     </ClInclude>
index 67e8a29..4a5164c 100644 (file)
                70ECA6071AFDBEA200449739 /* TemplateRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70ECA6021AFDBEA200449739 /* TemplateRegistry.cpp */; };
                70ECA6081AFDBEA200449739 /* TemplateRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 70ECA6031AFDBEA200449739 /* TemplateRegistry.h */; settings = {ATTRIBUTES = (Private, ); }; };
                70ECA6091AFDBEA200449739 /* TemplateRegistryKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 70ECA6041AFDBEA200449739 /* TemplateRegistryKey.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               79EE0BFF1B4AFB85000385C9 /* VariableEnvironment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79EE0BFD1B4AFB85000385C9 /* VariableEnvironment.cpp */; };
+               79EE0C001B4AFB85000385C9 /* VariableEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = 79EE0BFE1B4AFB85000385C9 /* VariableEnvironment.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7C008CDA187124BB00955C24 /* JSPromiseDeferred.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C008CD8187124BB00955C24 /* JSPromiseDeferred.cpp */; };
                7C008CDB187124BB00955C24 /* JSPromiseDeferred.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C008CD9187124BB00955C24 /* JSPromiseDeferred.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7C008CE7187631B600955C24 /* Microtask.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C008CE5187631B600955C24 /* Microtask.h */; settings = {ATTRIBUTES = (Private, ); }; };
                70ECA6021AFDBEA200449739 /* TemplateRegistry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TemplateRegistry.cpp; sourceTree = "<group>"; };
                70ECA6031AFDBEA200449739 /* TemplateRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemplateRegistry.h; sourceTree = "<group>"; };
                70ECA6041AFDBEA200449739 /* TemplateRegistryKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemplateRegistryKey.h; sourceTree = "<group>"; };
+               79EE0BFD1B4AFB85000385C9 /* VariableEnvironment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VariableEnvironment.cpp; sourceTree = "<group>"; };
+               79EE0BFE1B4AFB85000385C9 /* VariableEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VariableEnvironment.h; sourceTree = "<group>"; };
                7C008CD8187124BB00955C24 /* JSPromiseDeferred.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPromiseDeferred.cpp; sourceTree = "<group>"; };
                7C008CD9187124BB00955C24 /* JSPromiseDeferred.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPromiseDeferred.h; sourceTree = "<group>"; };
                7C008CE5187631B600955C24 /* Microtask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Microtask.h; sourceTree = "<group>"; };
                                E49DC15112EF272200184A1F /* SourceProviderCache.h */,
                                E49DC14912EF261A00184A1F /* SourceProviderCacheItem.h */,
                                A7A7EE7711B98B8D0065A14F /* SyntaxChecker.h */,
+                               79EE0BFD1B4AFB85000385C9 /* VariableEnvironment.cpp */,
+                               79EE0BFE1B4AFB85000385C9 /* VariableEnvironment.h */,
                        );
                        path = parser;
                        sourceTree = "<group>";
                                FE5068651AE246390009DAB7 /* DeferredSourceDump.h in Headers */,
                                C442CB251A6CDB8C005D3D7C /* JSInputs.json in Headers */,
                                FE1C0FFD1B193E9800B53FCA /* Exception.h in Headers */,
+                               79EE0C001B4AFB85000385C9 /* VariableEnvironment.h in Headers */,
                                52678F911A04177C006A306D /* ControlFlowProfiler.h in Headers */,
                                52678F8F1A031009006A306D /* BasicBlockLocation.h in Headers */,
                                A5EA710E19F6DF810098F5EC /* InspectorAlternateBackendDispatchers.h in Headers */,
                                0F8F94401667633000D61971 /* CodeBlockHash.cpp in Sources */,
                                0FC97F33182020D7002C9B26 /* CodeBlockJettisoningWatchpoint.cpp in Sources */,
                                0FD8A31317D4326C00CA2C40 /* CodeBlockSet.cpp in Sources */,
+                               79EE0BFF1B4AFB85000385C9 /* VariableEnvironment.cpp in Sources */,
                                A77F1821164088B200640A47 /* CodeCache.cpp in Sources */,
                                0F8F9446166764F100D61971 /* CodeOrigin.cpp in Sources */,
                                86B5826714D2796C00A9C306 /* CodeProfile.cpp in Sources */,
index 245eff3..40abe02 100644 (file)
@@ -103,7 +103,8 @@ UnlinkedFunctionExecutable* BuiltinExecutables::createExecutableInternal(const S
             continue;
     }
     body->overrideName(name);
-    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&m_vm, source, body, kind, WTF::move(sourceOverride));
+    VariableEnvironment dummyTDZVariables;
+    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&m_vm, source, body, kind, dummyTDZVariables, WTF::move(sourceOverride));
     functionExecutable->m_nameValue.set(m_vm, functionExecutable, jsString(&m_vm, name.string()));
     return functionExecutable;
 }
index 9a1fdd1..6692cfb 100644 (file)
@@ -4,7 +4,6 @@
         "macroNameComponent" : "BYTECODE", "asmPrefix" : "llint_", 
         "bytecodes" : [
             { "name" : "op_enter", "length" : 1 },
-            { "name" : "op_create_lexical_environment", "length" : 3 },
             { "name" : "op_get_scope", "length" : 2 },
             { "name" : "op_create_direct_arguments", "length" : 2 },
             { "name" : "op_create_scoped_arguments", "length" : 3 },
             { "name" : "op_get_from_arguments", "length" : 5 },
             { "name" : "op_put_to_arguments", "length" : 4 },
             { "name" : "op_push_with_scope", "length" : 3 },
-            { "name" : "op_pop_scope", "length" : 2 },
             { "name" : "op_push_name_scope", "length" : 5 },
+            { "name" : "op_create_lexical_environment", "length" : 5 },
+            { "name" : "op_get_parent_scope", "length" : 3 },
             { "name" : "op_catch", "length" : 3 },
             { "name" : "op_throw", "length" : 2 },
             { "name" : "op_throw_static_error", "length" : 3 },
index 250d322..413d84f 100644 (file)
@@ -57,7 +57,6 @@ void computeUsesForBytecodeOffset(
     case op_get_scope:
     case op_to_this:
     case op_check_tdz:
-    case op_pop_scope:
     case op_profile_will_call:
     case op_profile_did_call:
     case op_profile_type:
@@ -113,7 +112,6 @@ void computeUsesForBytecodeOffset(
         functor(codeBlock, instruction, opcodeID, instruction[4].u.operand);
         return;
     }
-    case op_create_lexical_environment:
     case op_get_property_enumerator:
     case op_get_enumerable_length:
     case op_new_func_exp:
@@ -122,6 +120,7 @@ void computeUsesForBytecodeOffset(
     case op_init_global_const:
     case op_push_name_scope:
     case op_push_with_scope:
+    case op_create_lexical_environment:
     case op_resolve_scope:
     case op_get_from_scope:
     case op_to_primitive:
@@ -148,6 +147,7 @@ void computeUsesForBytecodeOffset(
     case op_del_by_id:
     case op_unsigned:
     case op_new_func:
+    case op_get_parent_scope:
     case op_create_scoped_arguments:
     case op_get_from_arguments: {
         functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
@@ -297,9 +297,10 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset,
     case op_get_property_enumerator:
     case op_enumerator_structure_pname:
     case op_enumerator_generic_pname:
-    case op_pop_scope:
+    case op_get_parent_scope:
     case op_push_name_scope:
     case op_push_with_scope:
+    case op_create_lexical_environment:
     case op_resolve_scope:
     case op_strcat:
     case op_to_primitive:
@@ -373,8 +374,7 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset,
         functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
         return;
     }
-    case op_catch:
-    case op_create_lexical_environment: {
+    case op_catch: {
         functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
         functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
         return;
index 3ad7527..00eb283 100644 (file)
@@ -763,13 +763,6 @@ void CodeBlock::dumpBytecode(
             printLocationAndOp(out, exec, location, it, "enter");
             break;
         }
-        case op_create_lexical_environment: {
-            int r0 = (++it)->u.operand;
-            int r1 = (++it)->u.operand;
-            printLocationAndOp(out, exec, location, it, "create_lexical_environment");
-            out.printf("%s, %s", registerName(r0).data(), registerName(r1).data());
-            break;
-        }
         case op_get_scope: {
             int r0 = (++it)->u.operand;
             printLocationOpAndRegisterOperand(out, exec, location, it, "get_scope", r0);
@@ -1468,9 +1461,11 @@ void CodeBlock::dumpBytecode(
             out.printf("%s, %s", registerName(dst).data(), registerName(newScope).data());
             break;
         }
-        case op_pop_scope: {
-            int r0 = (++it)->u.operand;
-            printLocationOpAndRegisterOperand(out, exec, location, it, "pop_scope", r0);
+        case op_get_parent_scope: {
+            int dst = (++it)->u.operand;
+            int parentScope = (++it)->u.operand;
+            printLocationAndOp(out, exec, location, it, "get_parent_scope");
+            out.printf("%s, %s", registerName(dst).data(), registerName(parentScope).data());
             break;
         }
         case op_push_name_scope: {
@@ -1482,6 +1477,16 @@ void CodeBlock::dumpBytecode(
             out.printf("%s, %s, %s, %s", registerName(dst).data(), registerName(r1).data(), constantName(k0).data(), (scopeType == JSNameScope::FunctionNameScope) ? "functionScope" : ((scopeType == JSNameScope::CatchScope) ? "catchScope" : "unknownScopeType"));
             break;
         }
+        case op_create_lexical_environment: {
+            int dst = (++it)->u.operand;
+            int scope = (++it)->u.operand;
+            int symbolTable = (++it)->u.operand;
+            int initialValue = (++it)->u.operand;
+            printLocationAndOp(out, exec, location, it, "create_lexical_environment");
+            out.printf("%s, %s, %s, %s", 
+                registerName(dst).data(), registerName(scope).data(), registerName(symbolTable).data(), registerName(initialValue).data());
+            break;
+        }
         case op_catch: {
             int r0 = (++it)->u.operand;
             int r1 = (++it)->u.operand;
@@ -1706,9 +1711,8 @@ CodeBlock::CodeBlock(CopyParsedBlockTag, CodeBlock& other)
     ASSERT(m_heap->isDeferred());
     ASSERT(m_scopeRegister.isLocal());
 
-    if (SymbolTable* symbolTable = other.symbolTable())
-        m_symbolTable.set(*m_vm, m_ownerExecutable.get(), symbolTable);
-    
+    m_symbolTableConstantIndex = other.m_symbolTableConstantIndex;
+
     setNumParameters(other.numParameters());
     optimizeAfterWarmUp();
     jitAfterWarmUp();
@@ -1765,19 +1769,6 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
 
     bool didCloneSymbolTable = false;
     
-    if (SymbolTable* symbolTable = unlinkedCodeBlock->symbolTable()) {
-        if (m_vm->typeProfiler()) {
-            ConcurrentJITLocker locker(symbolTable->m_lock);
-            symbolTable->prepareForTypeProfiling(locker);
-        }
-
-        if (codeType() == FunctionCode && symbolTable->scopeSize()) {
-            m_symbolTable.set(*m_vm, m_ownerExecutable.get(), symbolTable->cloneScopePart(*m_vm));
-            didCloneSymbolTable = true;
-        } else
-            m_symbolTable.set(*m_vm, m_ownerExecutable.get(), symbolTable);
-    }
-    
     ASSERT(m_source);
     setNumParameters(unlinkedCodeBlock->numParameters());
 
@@ -1793,6 +1784,24 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
         if (unsigned registerIndex = unlinkedCodeBlock->registerIndexForLinkTimeConstant(type))
             m_constantRegisters[registerIndex].set(*m_vm, ownerExecutable, m_globalObject->jsCellForLinkTimeConstant(type));
     }
+    
+    if (SymbolTable* symbolTable = unlinkedCodeBlock->symbolTable()) {
+        if (m_vm->typeProfiler()) {
+            ConcurrentJITLocker locker(symbolTable->m_lock);
+            symbolTable->prepareForTypeProfiling(locker);
+        }
+
+        SymbolTable* newTable;
+        if (codeType() == FunctionCode && symbolTable->scopeSize()) {
+            newTable = symbolTable->cloneScopePart(*m_vm);
+            didCloneSymbolTable = true;
+        } else
+            newTable = symbolTable;
+
+        m_symbolTableConstantIndex = unlinkedCodeBlock->symbolTableConstantIndex();
+        replaceConstant(m_symbolTableConstantIndex, newTable);
+    } else 
+        m_symbolTableConstantIndex = 0;
 
     m_functionDecls.resizeToFit(unlinkedCodeBlock->numberOfFunctionDecls());
     for (size_t count = unlinkedCodeBlock->numberOfFunctionDecls(), i = 0; i < count; ++i) {
@@ -1875,6 +1884,9 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
     UnlinkedInstructionStream::Reader instructionReader(unlinkedCodeBlock->instructions());
 
     Vector<Instruction, 0, UnsafeVectorOverflow> instructions(instructionCount);
+
+    HashSet<int, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> clonedConstantSymbolTables;
+
     for (unsigned i = 0; !instructionReader.atEnd(); ) {
         const UnlinkedInstruction* pc = instructionReader.next();
 
@@ -1983,12 +1995,15 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
             const Identifier& ident = identifier(pc[3].u.operand);
             ResolveType type = static_cast<ResolveType>(pc[4].u.operand);
             RELEASE_ASSERT(type != LocalClosureVar);
+            int localScopeDepth = pc[5].u.operand;
 
-            ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), needsActivation(), scope, ident, Get, type);
+            ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), localScopeDepth, scope, ident, Get, type);
             instructions[i + 4].u.operand = op.type;
             instructions[i + 5].u.operand = op.depth;
             if (op.lexicalEnvironment)
                 instructions[i + 6].u.symbolTable.set(*vm(), ownerExecutable, op.lexicalEnvironment->symbolTable());
+            else
+                instructions[i + 6].u.pointer = nullptr;
             break;
         }
 
@@ -2000,6 +2015,9 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
 
             // get_from_scope dst, scope, id, ResolveModeAndType, Structure, Operand
 
+            int localScopeDepth = pc[5].u.operand;
+            instructions[i + 5].u.pointer = nullptr;
+
             ResolveModeAndType modeAndType = ResolveModeAndType(pc[4].u.operand);
             if (modeAndType.type() == LocalClosureVar) {
                 instructions[i + 4] = ResolveModeAndType(modeAndType.mode(), ClosureVar).operand();
@@ -2007,8 +2025,7 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
             }
 
             const Identifier& ident = identifier(pc[3].u.operand);
-
-            ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), needsActivation(), scope, ident, Get, modeAndType.type());
+            ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), localScopeDepth, scope, ident, Get, modeAndType.type());
 
             instructions[i + 4].u.operand = ResolveModeAndType(modeAndType.mode(), op.type).operand();
             if (op.type == GlobalVar || op.type == GlobalVarWithVarInjectionChecks)
@@ -2025,11 +2042,34 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
             if (modeAndType.type() == LocalClosureVar) {
                 // Only do watching if the property we're putting to is not anonymous.
                 if (static_cast<unsigned>(pc[2].u.operand) != UINT_MAX) {
-                    RELEASE_ASSERT(didCloneSymbolTable);
+                    // Different create_lexical_environment instructions may refer to the same symbol table. 
+                    // This is used for ES6's 'for' loops each having a separate activation. We will emit two 
+                    // create_lexical_environment instructions for a given loop to implement this feature, 
+                    // but both instructions should rely on the same underlying symbol table so that the 
+                    // loop's scope isn't mistakenly inferred as a singleton scope.
+                    int symbolTableIndex = pc[5].u.operand;
+                    auto addResult = clonedConstantSymbolTables.add(symbolTableIndex);
+                    if (addResult.isNewEntry) {
+                        SymbolTable* unlinkedTable = jsCast<SymbolTable*>(getConstant(symbolTableIndex));
+                        SymbolTable* linkedTable;
+                        if (unlinkedTable->correspondsToLexicalScope()) {
+                            RELEASE_ASSERT(unlinkedTable->scopeSize());
+                            linkedTable = unlinkedTable->cloneScopePart(*m_vm);
+                        } else {
+                            // There is only one SymbolTable per function that does not correspond
+                            // to a lexical scope and that is the function's var symbol table.
+                            // We've already cloned that.
+                            linkedTable = symbolTable();
+                            if (linkedTable->scopeSize())
+                                RELEASE_ASSERT(didCloneSymbolTable);
+                        }
+                        replaceConstant(symbolTableIndex, linkedTable);
+                    }
+                    SymbolTable* symbolTable = jsCast<SymbolTable*>(getConstant(symbolTableIndex));
                     const Identifier& ident = identifier(pc[2].u.operand);
-                    ConcurrentJITLocker locker(m_symbolTable->m_lock);
-                    SymbolTable::Map::iterator iter = m_symbolTable->find(locker, ident.impl());
-                    ASSERT(iter != m_symbolTable->end(locker));
+                    ConcurrentJITLocker locker(symbolTable->m_lock);
+                    auto iter = symbolTable->find(locker, ident.impl());
+                    RELEASE_ASSERT(iter != symbolTable->end(locker));
                     iter->value.prepareToWatch();
                     instructions[i + 5].u.watchpointSet = iter->value.watchpointSet();
                 } else
@@ -2038,8 +2078,9 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
             }
 
             const Identifier& ident = identifier(pc[2].u.operand);
-
-            ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), needsActivation(), scope, ident, Put, modeAndType.type());
+            int localScopeDepth = pc[5].u.operand;
+            instructions[i + 5].u.pointer = nullptr;
+            ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), localScopeDepth, scope, ident, Put, modeAndType.type());
 
             instructions[i + 4].u.operand = ResolveModeAndType(modeAndType.mode(), op.type).operand();
             if (op.type == GlobalVar || op.type == GlobalVarWithVarInjectionChecks)
@@ -2065,13 +2106,14 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
             VirtualRegister profileRegister(pc[1].u.operand);
             ProfileTypeBytecodeFlag flag = static_cast<ProfileTypeBytecodeFlag>(pc[3].u.operand);
             SymbolTable* symbolTable = nullptr;
+            int localScopeDepth = pc[2].u.operand;
 
             switch (flag) {
             case ProfileTypeBytecodePutToScope:
             case ProfileTypeBytecodeGetFromScope: {
                 const Identifier& ident = identifier(pc[4].u.operand);
                 ResolveType type = static_cast<ResolveType>(pc[5].u.operand);
-                ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), needsActivation(), scope, ident, (flag == ProfileTypeBytecodeGetFromScope ? Get : Put), type);
+                ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), localScopeDepth, scope, ident, (flag == ProfileTypeBytecodeGetFromScope ? Get : Put), type);
 
                 // FIXME: handle other values for op.type here, and also consider what to do when we can't statically determine the globalID
                 // https://bugs.webkit.org/show_bug.cgi?id=135184
@@ -2094,7 +2136,7 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
             case ProfileTypeBytecodePutToLocalScope:
             case ProfileTypeBytecodeGetFromLocalScope: {
                 const Identifier& ident = identifier(pc[4].u.operand);
-                symbolTable = m_symbolTable.get();
+                symbolTable = this->symbolTable();
                 ConcurrentJITLocker locker(symbolTable->m_lock);
                 // If our parent scope was created while profiling was disabled, it will not have prepared for profiling yet.
                 symbolTable->prepareForTypeProfiling(locker);
@@ -2105,7 +2147,7 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
             }
 
             case ProfileTypeBytecodeHasGlobalID: {
-                symbolTable = m_symbolTable.get();
+                symbolTable = this->symbolTable();
                 ConcurrentJITLocker locker(symbolTable->m_lock);
                 globalVariableID = symbolTable->uniqueIDForOffset(locker, VarOffset(profileRegister), *vm());
                 globalTypeSet = symbolTable->globalTypeSetForOffset(locker, VarOffset(profileRegister), *vm());
@@ -2787,7 +2829,6 @@ void CodeBlock::stronglyVisitStrongReferences(SlotVisitor& visitor)
 {
     visitor.append(&m_globalObject);
     visitor.append(&m_ownerExecutable);
-    visitor.append(&m_symbolTable);
     visitor.append(&m_unlinkedCode);
     if (m_rareData)
         m_rareData->m_evalCodeCache.visitAggregate(visitor);
index 65dab95..16c58c3 100644 (file)
@@ -664,8 +664,7 @@ public:
     StringJumpTable& addStringSwitchJumpTable() { createRareDataIfNecessary(); m_rareData->m_stringSwitchJumpTables.append(StringJumpTable()); return m_rareData->m_stringSwitchJumpTables.last(); }
     StringJumpTable& stringSwitchJumpTable(int tableIndex) { RELEASE_ASSERT(m_rareData); return m_rareData->m_stringSwitchJumpTables[tableIndex]; }
 
-
-    SymbolTable* symbolTable() const { return m_symbolTable.get(); }
+    SymbolTable* symbolTable() const { RELEASE_ASSERT(m_symbolTableConstantIndex); return jsCast<SymbolTable*>(getConstant(m_symbolTableConstantIndex)); }
 
     EvalCodeCache& evalCodeCache() { createRareDataIfNecessary(); return m_rareData->m_evalCodeCache; }
 
@@ -937,6 +936,12 @@ private:
         m_constantsSourceCodeRepresentation = constantsSourceCodeRepresentation;
     }
 
+    void replaceConstant(int index, JSValue value)
+    {
+        ASSERT(isConstantRegisterIndex(index) && static_cast<size_t>(index - FirstConstantRegisterIndex) < m_constantRegisters.size());
+        m_constantRegisters[index - FirstConstantRegisterIndex].set(m_globalObject->vm(), m_ownerExecutable.get(), value);
+    }
+
     void dumpBytecode(
         PrintStream&, ExecState*, const Instruction* begin, const Instruction*&,
         const StubInfoMap& = StubInfoMap(), const CallLinkInfoMap& = CallLinkInfoMap());
@@ -994,7 +999,7 @@ private:
     VM* m_vm;
 
     RefCountedArray<Instruction> m_instructions;
-    WriteBarrier<SymbolTable> m_symbolTable;
+    int m_symbolTableConstantIndex;
     VirtualRegister m_thisRegister;
     VirtualRegister m_scopeRegister;
     VirtualRegister m_lexicalEnvironmentRegister;
index b0e5aac..3a02ef9 100644 (file)
@@ -45,18 +45,20 @@ namespace JSC {
     public:
         EvalExecutable* tryGet(bool inStrictContext, const String& evalSource, JSScope* scope)
         {
-            if (!inStrictContext && evalSource.length() < Options::maximumEvalCacheableSourceLength() && scope->begin()->isVariableObject())
+            if (isCacheable(inStrictContext, evalSource, scope))
                 return m_cacheMap.get(evalSource.impl()).get();
             return 0;
         }
         
         EvalExecutable* getSlow(ExecState* exec, ScriptExecutable* owner, bool inStrictContext, ThisTDZMode thisTDZMode, const String& evalSource, JSScope* scope)
         {
-            EvalExecutable* evalExecutable = EvalExecutable::create(exec, makeSource(evalSource), inStrictContext, thisTDZMode);
+            VariableEnvironment variablesUnderTDZ;
+            JSScope::collectVariablesUnderTDZ(scope, variablesUnderTDZ);
+            EvalExecutable* evalExecutable = EvalExecutable::create(exec, makeSource(evalSource), inStrictContext, thisTDZMode, &variablesUnderTDZ);
             if (!evalExecutable)
                 return 0;
 
-            if (!inStrictContext && evalSource.length() < Options::maximumEvalCacheableSourceLength() && scope->begin()->isVariableObject() && m_cacheMap.size() < maxCacheEntries)
+            if (isCacheable(inStrictContext, evalSource, scope) && m_cacheMap.size() < maxCacheEntries)
                 m_cacheMap.set(evalSource.impl(), WriteBarrier<EvalExecutable>(exec->vm(), owner, evalExecutable));
             
             return evalExecutable;
@@ -72,6 +74,15 @@ namespace JSC {
         }
 
     private:
+        ALWAYS_INLINE bool isCacheable(bool inStrictContext, const String& evalSource, JSScope* scope) const
+        {
+            // If eval() is called and it has access to a lexical scope, we can't soundly cache it.
+            // If the eval() only has access to the "var" scope, then we can cache it.
+            return !inStrictContext 
+                && evalSource.length() < Options::maximumEvalCacheableSourceLength() 
+                && scope->begin()->isVariableObject()
+                && !scope->isLexicalScope();
+        }
         static const int maxCacheEntries = 64;
 
         typedef HashMap<RefPtr<StringImpl>, WriteBarrier<EvalExecutable>> EvalCacheMap;
index e262bc5..e8849bf 100644 (file)
@@ -43,7 +43,7 @@
 
 namespace JSC {
 
-static_assert(sizeof(UnlinkedFunctionExecutable) <= 128, "UnlinkedFunctionExecutable should fit in a 128-byte cell.");
+static_assert(sizeof(UnlinkedFunctionExecutable) <= 256, "UnlinkedFunctionExecutable should fit in a 256-byte cell.");
 
 const ClassInfo UnlinkedFunctionExecutable::s_info = { "UnlinkedFunctionExecutable", 0, 0, CREATE_METHOD_TABLE(UnlinkedFunctionExecutable) };
 const ClassInfo UnlinkedCodeBlock::s_info = { "UnlinkedCodeBlock", 0, 0, CREATE_METHOD_TABLE(UnlinkedCodeBlock) };
@@ -73,14 +73,14 @@ static UnlinkedFunctionCodeBlock* generateFunctionCodeBlock(
     
     UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode,
         ExecutableInfo(function->needsActivation(), function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction, executable->constructorKind()));
-    auto generator(std::make_unique<BytecodeGenerator>(vm, function.get(), result, debuggerMode, profilerMode));
+    auto generator(std::make_unique<BytecodeGenerator>(vm, function.get(), result, debuggerMode, profilerMode, executable->parentScopeTDZVariables()));
     error = generator->generate();
     if (error.isValid())
         return nullptr;
     return result;
 }
 
-UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* structure, const SourceCode& source, RefPtr<SourceProvider>&& sourceOverride, FunctionBodyNode* node, UnlinkedFunctionKind kind)
+UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* structure, const SourceCode& source, RefPtr<SourceProvider>&& sourceOverride, FunctionBodyNode* node, UnlinkedFunctionKind kind, VariableEnvironment& parentScopeTDZVariables)
     : Base(*vm, structure)
     , m_name(node->ident())
     , m_inferredName(node->inferredName())
@@ -104,6 +104,7 @@ UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* struct
     , m_functionMode(node->functionMode())
 {
     ASSERT(m_constructorKind == static_cast<unsigned>(node->constructorKind()));
+    m_parentScopeTDZVariables.swap(parentScopeTDZVariables);
 }
 
 size_t UnlinkedFunctionExecutable::parameterCount() const
index 7904424..a6a9a21 100644 (file)
@@ -38,6 +38,7 @@
 #include "RegExp.h"
 #include "SpecialPointer.h"
 #include "SymbolTable.h"
+#include "VariableEnvironment.h"
 #include "VirtualRegister.h"
 
 #include <wtf/RefCountedArray.h>
@@ -107,10 +108,10 @@ public:
     typedef JSCell Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
 
-    static UnlinkedFunctionExecutable* create(VM* vm, const SourceCode& source, FunctionBodyNode* node, UnlinkedFunctionKind unlinkedFunctionKind, RefPtr<SourceProvider>&& sourceOverride = nullptr)
+    static UnlinkedFunctionExecutable* create(VM* vm, const SourceCode& source, FunctionBodyNode* node, UnlinkedFunctionKind unlinkedFunctionKind, VariableEnvironment& parentScopeTDZVariables, RefPtr<SourceProvider>&& sourceOverride = nullptr)
     {
         UnlinkedFunctionExecutable* instance = new (NotNull, allocateCell<UnlinkedFunctionExecutable>(vm->heap))
-            UnlinkedFunctionExecutable(vm, vm->unlinkedFunctionExecutableStructure.get(), source, WTF::move(sourceOverride), node, unlinkedFunctionKind);
+            UnlinkedFunctionExecutable(vm, vm->unlinkedFunctionExecutableStructure.get(), source, WTF::move(sourceOverride), node, unlinkedFunctionKind, parentScopeTDZVariables);
         instance->finishCreation(*vm);
         return instance;
     }
@@ -170,9 +171,10 @@ public:
 
     bool isBuiltinFunction() const { return m_isBuiltinFunction; }
     bool isClassConstructorFunction() const { return constructorKind() != ConstructorKind::None; }
+    const VariableEnvironment* parentScopeTDZVariables() const { return &m_parentScopeTDZVariables; }
 
 private:
-    UnlinkedFunctionExecutable(VM*, Structure*, const SourceCode&, RefPtr<SourceProvider>&& sourceOverride, FunctionBodyNode*, UnlinkedFunctionKind);
+    UnlinkedFunctionExecutable(VM*, Structure*, const SourceCode&, RefPtr<SourceProvider>&& sourceOverride, FunctionBodyNode*, UnlinkedFunctionKind, VariableEnvironment&);
     WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForCall;
     WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForConstruct;
 
@@ -183,6 +185,7 @@ private:
     WriteBarrier<SymbolTable> m_symbolTableForConstruct;
     RefPtr<FunctionParameters> m_parameters;
     RefPtr<SourceProvider> m_sourceOverride;
+    VariableEnvironment m_parentScopeTDZVariables;
     unsigned m_firstLineOffset;
     unsigned m_lineCount;
     unsigned m_unlinkedFunctionNameStart;
@@ -423,6 +426,8 @@ public:
 
     SymbolTable* symbolTable() const { return m_symbolTable.get(); }
     void setSymbolTable(SymbolTable* table) { m_symbolTable.set(*m_vm, this, table); }
+    void setSymbolTableConstantIndex(int index) { m_symbolTableConstantIndex = index; }
+    int symbolTableConstantIndex() const { return m_symbolTableConstantIndex; }
 
     VM* vm() const { return m_vm; }
 
@@ -566,6 +571,7 @@ private:
     FunctionExpressionVector m_functionExprs;
 
     WriteBarrier<SymbolTable> m_symbolTable;
+    int m_symbolTableConstantIndex;
 
     Vector<unsigned> m_propertyAccessInstructions;
 
@@ -645,14 +651,8 @@ public:
 
     static void destroy(JSCell*);
 
-    void addVariableDeclaration(const Identifier& name, bool isConstant)
-    {
-        m_varDeclarations.append(std::make_pair(name, isConstant));
-    }
-
-    typedef Vector<std::pair<Identifier, bool>> VariableDeclations;
-
-    const VariableDeclations& variableDeclarations() const { return m_varDeclarations; }
+    void setVariableDeclarations(const VariableEnvironment& environment) { m_varDeclarations = environment; }
+    const VariableEnvironment& variableDeclarations() const { return m_varDeclarations; }
 
     static void visitChildren(JSCell*, SlotVisitor&);
 
@@ -662,7 +662,7 @@ private:
     {
     }
 
-    VariableDeclations m_varDeclarations;
+    VariableEnvironment m_varDeclarations;
 
 public:
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
index 8b2f8e6..efbf8e7 100644 (file)
@@ -76,18 +76,33 @@ ParserError BytecodeGenerator::generate()
         entry.second->bindValue(*this, entry.first.get());
     }
 
+    pushLexicalScope(m_scopeNode, true);
+
     {
         RefPtr<RegisterID> temp = newTemporary();
-        RefPtr<RegisterID> globalScope = scopeRegister(); // FIXME: With lexical scoping, this won't always be the global object: https://bugs.webkit.org/show_bug.cgi?id=142944 
+        RefPtr<RegisterID> globalScope;
         for (auto functionPair : m_functionsToInitialize) {
             FunctionBodyNode* functionBody = functionPair.first;
             FunctionVariableType functionType = functionPair.second;
             emitNewFunction(temp.get(), functionBody);
             if (functionType == NormalFunctionVariable)
                 initializeVariable(variable(functionBody->ident()) , temp.get());
-            else if (functionType == GlobalFunctionVariable)
+            else if (functionType == GlobalFunctionVariable) {
+                if (!globalScope) {
+                    if (m_symbolTableStack.isEmpty() || !m_symbolTableStack.first().m_scope) {
+                        // We haven't allocated a lexical scope on top of the global object, so scopeRegister() will correspond to the global scope.
+                        globalScope = scopeRegister();
+                    } else {
+                        // We know this will resolve to the global object because our parser doesn't allow
+                        // "let" variables to have the same names as functions.
+                        ASSERT(m_scopeNode->lexicalVariables().hasCapturedVariables());
+                        RefPtr<RegisterID> globalObjectScope = emitResolveScope(nullptr, Variable(functionBody->ident()));
+                        globalScope = newBlockScopeVariable();
+                        emitMove(globalScope.get(), globalObjectScope.get());
+                    }
+                }
                 emitPutToScope(globalScope.get(), Variable(functionBody->ident()), temp.get(), ThrowIfNotFound);
-            else
+            else
                 RELEASE_ASSERT_NOT_REACHED();
         }
     }
@@ -148,7 +163,7 @@ ParserError BytecodeGenerator::generate()
     return ParserError(ParserError::ErrorNone);
 }
 
-BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode)
+BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode, const VariableEnvironment* parentScopeTDZVariables)
     : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn)
     , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn)
     , m_scopeNode(programNode)
@@ -157,6 +172,8 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedP
     , m_codeType(GlobalCode)
     , m_vm(&vm)
 {
+    ASSERT_UNUSED(parentScopeTDZVariables, !parentScopeTDZVariables->size());
+
     for (auto& constantRegister : m_linkTimeConstantRegisters)
         constantRegister = nullptr;
 
@@ -166,20 +183,22 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedP
 
     allocateAndEmitScope();
 
-    const VarStack& varStack = programNode->varStack();
     const FunctionStack& functionStack = programNode->functionStack();
 
     for (size_t i = 0; i < functionStack.size(); ++i) {
         FunctionBodyNode* function = functionStack[i];
         m_functionsToInitialize.append(std::make_pair(function, GlobalFunctionVariable));
     }
-
-    for (size_t i = 0; i < varStack.size(); ++i)
-        codeBlock->addVariableDeclaration(varStack[i].first, !!(varStack[i].second & DeclarationStacks::IsConstant));
-
+    if (Options::validateBytecode()) {
+        for (auto& entry : programNode->varDeclarations()) {
+            // FIXME: When supporting ES6 spec compliant const, this should only check isVar().
+            RELEASE_ASSERT(entry.value.isVar() || entry.value.isConstant());
+        }
+    }
+    codeBlock->setVariableDeclarations(programNode->varDeclarations());
 }
 
-BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, UnlinkedFunctionCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode)
+BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, UnlinkedFunctionCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode, const VariableEnvironment* parentScopeTDZVariables)
     : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn)
     , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn)
     , m_symbolTable(codeBlock->symbolTable())
@@ -209,10 +228,10 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
     bool shouldCaptureSomeOfTheThings = m_shouldEmitDebugHooks || m_codeBlock->needsFullScopeChain();
     bool shouldCaptureAllOfTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval();
     bool needsArguments = functionNode->usesArguments() || codeBlock->usesEval();
+    if (shouldCaptureAllOfTheThings)
+        functionNode->varDeclarations().markAllVariablesAsCaptured();
     
     auto captures = [&] (UniquedStringImpl* uid) -> bool {
-        if (shouldCaptureAllOfTheThings)
-            return true;
         if (!shouldCaptureSomeOfTheThings)
             return false;
         if (needsArguments && uid == propertyNames().arguments.impl()) {
@@ -244,18 +263,23 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=141887
         emitPushFunctionNameScope(m_scopeRegister, functionNode->ident(), &m_calleeRegister, ReadOnly | DontDelete);
     }
-
+    
+    int symbolTableConstantIndex = addConstantValue(m_symbolTable)->index();
+    m_codeBlock->setSymbolTableConstantIndex(symbolTableConstantIndex);
     if (shouldCaptureSomeOfTheThings) {
         m_lexicalEnvironmentRegister = addVar();
         m_codeBlock->setActivationRegister(m_lexicalEnvironmentRegister->virtualRegister());
         emitOpcode(op_create_lexical_environment);
         instructions().append(m_lexicalEnvironmentRegister->index());
         instructions().append(scopeRegister()->index());
+        instructions().append(symbolTableConstantIndex);
+        instructions().append(addConstantValue(jsUndefined())->index());
+
         emitOpcode(op_mov);
         instructions().append(scopeRegister()->index());
         instructions().append(m_lexicalEnvironmentRegister->index());
     }
-    
+
     // Make sure the code block knows about all of our parameters, and make sure that parameters
     // needing destructuring are noted.
     m_parameters.grow(parameters.size() + 1); // reserve space for "this"
@@ -329,7 +353,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
                 instructions().append(UINT_MAX);
                 instructions().append(virtualRegisterForArgument(1 + i).offset());
                 instructions().append(ResolveModeAndType(ThrowIfNotFound, LocalClosureVar).operand());
-                instructions().append(0);
+                instructions().append(symbolTableConstantIndex);
                 instructions().append(offset.offset());
             }
             
@@ -374,7 +398,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
             instructions().append(addConstant(ident));
             instructions().append(virtualRegisterForArgument(1 + i).offset());
             instructions().append(ResolveModeAndType(ThrowIfNotFound, LocalClosureVar).operand());
-            instructions().append(0);
+            instructions().append(symbolTableConstantIndex);
             instructions().append(offset.offset());
         }
     }
@@ -393,12 +417,14 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
         createVariable(ident, varKind(ident.impl()), IsVariable);
         m_functionsToInitialize.append(std::make_pair(function, NormalFunctionVariable));
     }
-    for (auto& entry : functionNode->varStack()) {
-        ConstantMode constantMode = modeForIsConstant(entry.second & DeclarationStacks::IsConstant);
+    for (auto& entry : functionNode->varDeclarations()) {
+        if (!entry.value.isVar())
+            continue;
+        ConstantMode constantMode = modeForIsConstant(entry.value.isConstant());
         // Variables named "arguments" are never const.
-        if (entry.first == propertyNames().arguments)
+        if (Identifier::fromUid(m_vm, entry.key.get()) == propertyNames().arguments)
             constantMode = IsVariable;
-        createVariable(entry.first, varKind(entry.first.impl()), constantMode, IgnoreExisting);
+        createVariable(Identifier::fromUid(m_vm, entry.key.get()), varKind(entry.key.get()), constantMode, IgnoreExisting);
     }
     
     // There are some variables that need to be preinitialized to something other than Undefined:
@@ -443,7 +469,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
             instructions().append(addConstant(functionNode->ident()));
             instructions().append(m_calleeRegister.index());
             instructions().append(ResolveModeAndType(ThrowIfNotFound, LocalClosureVar).operand());
-            instructions().append(0);
+            instructions().append(symbolTableConstantIndex);
             instructions().append(offset.offset());
         } else {
             m_symbolTable->add(
@@ -495,9 +521,12 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
         instructions().append(0);
         instructions().append(0);
     }
+
+    m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(*m_vm, m_symbolTable), m_lexicalEnvironmentRegister, false, symbolTableConstantIndex });
+    m_TDZStack.append(std::make_pair(*parentScopeTDZVariables, false));
 }
 
-BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode)
+BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode, const VariableEnvironment* parentScopeTDZVariables)
     : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn)
     , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn)
     , m_symbolTable(codeBlock->symbolTable())
@@ -512,6 +541,8 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCod
 
     m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode());
     m_codeBlock->setNumParameters(1);
+    int symbolTableConstantIndex = addConstantValue(m_symbolTable)->index();
+    m_codeBlock->setSymbolTableConstantIndex(symbolTableConstantIndex);
 
     emitOpcode(op_enter);
 
@@ -521,15 +552,18 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCod
     for (size_t i = 0; i < functionStack.size(); ++i)
         m_codeBlock->addFunctionDecl(makeFunction(functionStack[i]));
 
-    const DeclarationStacks::VarStack& varStack = evalNode->varStack();
-    unsigned numVariables = varStack.size();
+    const VariableEnvironment& varDeclarations = evalNode->varDeclarations();
+    unsigned numVariables = varDeclarations.size();
     Vector<Identifier, 0, UnsafeVectorOverflow> variables;
     variables.reserveCapacity(numVariables);
-    for (size_t i = 0; i < numVariables; ++i) {
-        ASSERT(varStack[i].first.impl()->isAtomic() || varStack[i].first.impl()->isSymbol());
-        variables.append(varStack[i].first);
+    for (auto& entry : varDeclarations) {
+        ASSERT(entry.value.isVar() || entry.value.isConstant()); // FIXME: When supporting ES6 spec compliant const, this should only check isVar().
+        ASSERT(entry.key->isAtomic() || entry.key->isSymbol());
+        variables.append(Identifier::fromUid(m_vm, entry.key.get()));
     }
     codeBlock->adoptVariables(variables);
+
+    m_TDZStack.append(std::make_pair(*parentScopeTDZVariables, false));
 }
 
 BytecodeGenerator::~BytecodeGenerator()
@@ -564,12 +598,23 @@ RegisterID* BytecodeGenerator::newRegister()
     return &m_calleeRegisters.last();
 }
 
-RegisterID* BytecodeGenerator::newTemporary()
+void BytecodeGenerator::reclaimFreeRegisters()
 {
-    // Reclaim free register IDs.
     while (m_calleeRegisters.size() && !m_calleeRegisters.last().refCount())
         m_calleeRegisters.removeLast();
-        
+}
+
+RegisterID* BytecodeGenerator::newBlockScopeVariable()
+{
+    reclaimFreeRegisters();
+
+    return newRegister();
+}
+
+RegisterID* BytecodeGenerator::newTemporary()
+{
+    reclaimFreeRegisters();
+
     RegisterID* result = newRegister();
     result->setTemporary();
     return result;
@@ -1165,7 +1210,7 @@ void BytecodeGenerator::emitProfileType(RegisterID* registerToProfile, ProfileTy
     // The format of this instruction is: op_profile_type regToProfile, TypeLocation*, flag, identifier?, resolveType?
     emitOpcode(op_profile_type);
     instructions().append(registerToProfile->index());
-    instructions().append(0);
+    instructions().append(currentScopeDepth());
     instructions().append(flag);
     instructions().append(identifier ? addConstant(*identifier) : 0);
     instructions().append(resolveType());
@@ -1219,41 +1264,244 @@ RegisterID* BytecodeGenerator::emitLoadGlobalObject(RegisterID* dst)
     return m_globalObjectRegister;
 }
 
+void BytecodeGenerator::pushLexicalScope(VariableEnvironmentNode* node, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult)
+{
+    VariableEnvironment& environment = node->lexicalVariables();
+    if (!environment.size())
+        return;
+
+    if (m_shouldEmitDebugHooks)
+        environment.markAllVariablesAsCaptured();
+
+    Strong<SymbolTable> symbolTable(*m_vm, SymbolTable::create(*m_vm));
+    symbolTable->setDoesCorrespondToLexicalScope();
+    bool hasCapturedVariables = false;
+    {
+        ConcurrentJITLocker locker(symbolTable->m_lock);
+        for (auto entry : environment) {
+            ASSERT(entry.value.isLet() && !entry.value.isVar());
+            SymbolTableEntry symbolTableEntry = symbolTable->get(locker, entry.key.get());
+            ASSERT(symbolTableEntry.isNull());
+
+            VarKind varKind = entry.value.isCaptured() ? VarKind::Scope : VarKind::Stack;
+            VarOffset varOffset;
+            if (varKind == VarKind::Scope) {
+                varOffset = VarOffset(symbolTable->takeNextScopeOffset(locker));
+                hasCapturedVariables = true;
+            } else {
+                ASSERT(varKind == VarKind::Stack);
+                RegisterID* local = newBlockScopeVariable();
+                local->ref();
+                varOffset = VarOffset(local->virtualRegister());
+            }
+            // FIXME: Make this work with 'const' variables: https://bugs.webkit.org/show_bug.cgi?id=31813 
+            SymbolTableEntry newEntry(varOffset, 0);
+            symbolTable->add(locker, entry.key.get(), newEntry);
+        }
+    }
+
+    RegisterID* newScope = nullptr;
+    int symbolTableConstantIndex = 0;
+    if (hasCapturedVariables) {
+        newScope = newBlockScopeVariable();
+        newScope->ref();
+
+        RegisterID* constantSymbolTable = addConstantValue(symbolTable->cloneScopePart(*m_vm));
+        symbolTableConstantIndex = constantSymbolTable->index();
+        if (constantSymbolTableResult)
+            *constantSymbolTableResult = constantSymbolTable;
+
+        emitOpcode(op_create_lexical_environment);
+        instructions().append(newScope->index());
+        instructions().append(scopeRegister()->index());
+        instructions().append(constantSymbolTable->index());
+        instructions().append(addConstantValue(jsTDZValue())->index());
+
+        emitMove(scopeRegister(), newScope);
+    }
+
+    m_symbolTableStack.append(SymbolTableStackEntry{ symbolTable, newScope, false, symbolTableConstantIndex });
+    m_TDZStack.append(std::make_pair(environment, canOptimizeTDZChecks));
+    // Prefill stack variables with the TDZ empty value.
+    // Scope variables will be initialized to the TDZ empty value when JSLexicalEnvironment is allocated.
+    for (auto entry : environment) {
+        SymbolTableEntry symbolTableEntry = symbolTable->get(entry.key.get());
+        ASSERT(!symbolTableEntry.isNull());
+        VarOffset offset = symbolTableEntry.varOffset();
+        if (offset.isScope()) {
+            ASSERT(newScope);
+            continue;
+        }
+        ASSERT(offset.isStack());
+        emitMoveEmptyValue(&registerFor(offset.stackOffset()));
+    }
+}
+
+void BytecodeGenerator::popLexicalScope(VariableEnvironmentNode* node)
+{
+    VariableEnvironment& environment = node->lexicalVariables();
+    if (!environment.size())
+        return;
+
+    if (m_shouldEmitDebugHooks)
+        environment.markAllVariablesAsCaptured();
+
+    SymbolTableStackEntry stackEntry = m_symbolTableStack.takeLast();
+    Strong<SymbolTable> symbolTable = stackEntry.m_symbolTable;
+    ConcurrentJITLocker locker(symbolTable->m_lock);
+    bool hasCapturedVariables = false;
+    for (auto entry : environment) {
+        if (entry.value.isCaptured()) {
+            hasCapturedVariables = true;
+            continue;
+        }
+        SymbolTableEntry symbolTableEntry = symbolTable->get(locker, entry.key.get());
+        ASSERT(!symbolTableEntry.isNull());
+        VarOffset offset = symbolTableEntry.varOffset();
+        ASSERT(offset.isStack());
+        RegisterID* local = &registerFor(offset.stackOffset());
+        local->deref();
+    }
+
+    if (hasCapturedVariables) {
+        RELEASE_ASSERT(stackEntry.m_scope);
+        RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), scopeRegister());
+        emitMove(scopeRegister(), parentScope.get());
+        stackEntry.m_scope->deref();
+    }
+
+    m_TDZStack.removeLast();
+}
+
+void BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode* node, RegisterID* loopSymbolTable)
+{
+    VariableEnvironment& environment = node->lexicalVariables();
+    if (!environment.size())
+        return;
+    if (m_shouldEmitDebugHooks)
+        environment.markAllVariablesAsCaptured();
+    if (!environment.hasCapturedVariables())
+        return;
+
+    RELEASE_ASSERT(loopSymbolTable);
+
+    // This function needs to do setup for a for loop's activation if any of
+    // the for loop's lexically declared variables are captured (that is, variables
+    // declared in the loop header, not the loop body). This function needs to
+    // make a copy of the current activation and copy the values from the previous
+    // activation into the new activation because each iteration of a for loop
+    // gets a new activation.
+
+    SymbolTableStackEntry stackEntry = m_symbolTableStack.last();
+    Strong<SymbolTable> symbolTable = stackEntry.m_symbolTable;
+    RegisterID* loopScope = stackEntry.m_scope;
+    ASSERT(symbolTable->scopeSize());
+    ASSERT(loopScope);
+    Vector<std::pair<RegisterID*, Identifier>> activationValuesToCopyOver;
+
+    {
+        ConcurrentJITLocker locker(symbolTable->m_lock);
+        activationValuesToCopyOver.reserveInitialCapacity(symbolTable->scopeSize());
+
+        for (auto end = symbolTable->end(locker), ptr = symbolTable->begin(locker); ptr != end; ++ptr) {
+            if (!ptr->value.varOffset().isScope())
+                continue;
+
+            RefPtr<UniquedStringImpl> ident = ptr->key;
+            Identifier identifier = Identifier::fromUid(m_vm, ident.get());
+
+            RegisterID* transitionValue = newBlockScopeVariable();
+            transitionValue->ref();
+            emitGetFromScope(transitionValue, loopScope, variableForLocalEntry(identifier, ptr->value, loopSymbolTable->index()), DoNotThrowIfNotFound);
+            activationValuesToCopyOver.uncheckedAppend(std::make_pair(transitionValue, identifier));
+        }
+    }
+
+    // We need this dynamic behavior of the executing code to ensure
+    // each loop iteration has a new activation object. (It's pretty ugly).
+    // Also, this new activation needs to be assigned to the same register
+    // as the previous scope because the loop body is compiled under
+    // the assumption that the scope's register index is constant even
+    // though the value in that register will change on each loop iteration.
+    RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), scopeRegister());
+    emitMove(scopeRegister(), parentScope.get());
+
+    emitOpcode(op_create_lexical_environment);
+    instructions().append(loopScope->index());
+    instructions().append(scopeRegister()->index());
+    instructions().append(loopSymbolTable->index());
+    instructions().append(addConstantValue(jsTDZValue())->index());
+
+    emitMove(scopeRegister(), loopScope);
+
+    {
+        ConcurrentJITLocker locker(symbolTable->m_lock);
+        for (auto pair : activationValuesToCopyOver) {
+            const Identifier& identifier = pair.second;
+            SymbolTableEntry entry = symbolTable->get(locker, identifier.impl());
+            RELEASE_ASSERT(!entry.isNull());
+            RegisterID* transitionValue = pair.first;
+            emitPutToScope(loopScope, variableForLocalEntry(identifier, entry, loopSymbolTable->index()), transitionValue, DoNotThrowIfNotFound);
+            transitionValue->deref();
+        }
+    }
+}
+
 Variable BytecodeGenerator::variable(const Identifier& property)
 {
     if (property == propertyNames().thisIdentifier) {
         return Variable(
             property, VarOffset(thisRegister()->virtualRegister()), thisRegister(),
-            ReadOnly, Variable::SpecialVariable);
+            ReadOnly, Variable::SpecialVariable, 0);
     }
     
-    if (!shouldOptimizeLocals())
-        return Variable(property);
-    
-    SymbolTableEntry entry = symbolTable().get(property.impl());
-    if (entry.isNull())
-        return Variable(property);
-    
-    if (entry.varOffset().isScope() && m_localScopeDepth) {
-        // FIXME: We should be able to statically resolve through our local scopes.
-        // https://bugs.webkit.org/show_bug.cgi?id=141885
-        return Variable(property);
+    // We can optimize lookups if the lexical variable is found before a "with" or "catch"
+    // scope because we're guaranteed static resolution. If we have to pass through
+    // a "with" or "catch" scope we loose this guarantee.
+    // We can't optimize cases like this:
+    // {
+    //     let x = ...;
+    //     with (o) {
+    //         doSomethingWith(x);
+    //     }
+    // }
+    // Because we can't gaurantee static resolution on x.
+    // But, in this case, we are guaranteed static resolution:
+    // {
+    //     let x = ...;
+    //     with (o) {
+    //         let x = ...;
+    //         doSomethingWith(x);
+    //     }
+    // }
+    for (unsigned i = m_symbolTableStack.size(); i--; ) {
+        SymbolTableStackEntry& stackEntry = m_symbolTableStack[i];
+        if (stackEntry.m_isWithOrCatch)
+            return Variable(property);
+        Strong<SymbolTable>& symbolTable = stackEntry.m_symbolTable;
+        SymbolTableEntry symbolTableEntry = symbolTable->get(property.impl());
+        if (symbolTableEntry.isNull())
+            continue;
+        
+        return variableForLocalEntry(property, symbolTableEntry, stackEntry.m_symbolTableConstantIndex);
     }
-    
-    return variableForLocalEntry(property, entry);
+
+    return Variable(property);
 }
 
 Variable BytecodeGenerator::variablePerSymbolTable(const Identifier& property)
 {
-    SymbolTableEntry entry = symbolTable().get(property.impl());
+    RELEASE_ASSERT(m_symbolTableStack.size());
+    SymbolTableStackEntry& baseActivationEntry = m_symbolTableStack.first();
+    SymbolTableEntry entry = baseActivationEntry.m_symbolTable->get(property.impl());
     if (entry.isNull())
         return Variable(property);
     
-    return variableForLocalEntry(property, entry);
+    return variableForLocalEntry(property, entry, baseActivationEntry.m_symbolTableConstantIndex);
 }
 
 Variable BytecodeGenerator::variableForLocalEntry(
-    const Identifier& property, const SymbolTableEntry& entry)
+    const Identifier& property, const SymbolTableEntry& entry, int symbolTableConstantIndex)
 {
     VarOffset offset = entry.varOffset();
     
@@ -1263,7 +1511,7 @@ Variable BytecodeGenerator::variableForLocalEntry(
     else
         local = nullptr;
     
-    return Variable(property, offset, local, entry.getAttributes(), Variable::NormalVariable);
+    return Variable(property, offset, local, entry.getAttributes(), Variable::NormalVariable, symbolTableConstantIndex);
 }
 
 void BytecodeGenerator::createVariable(
@@ -1271,7 +1519,6 @@ void BytecodeGenerator::createVariable(
     ExistingVariableMode existingVariableMode)
 {
     ASSERT(property != propertyNames().thisIdentifier);
-    
     ConcurrentJITLocker locker(symbolTable().m_lock);
     SymbolTableEntry entry = symbolTable().get(locker, property.impl());
     
@@ -1348,7 +1595,28 @@ RegisterID* BytecodeGenerator::emitResolveScope(RegisterID* dst, const Variable&
         // don't do that already is that m_lexicalEnvironment is required by ConstDeclNode. ConstDeclNode
         // requires weird things because it is a shameful pile of nonsense, but block scoping would make
         // that code sensible and obviate the need for us to do bad things.
-        return m_lexicalEnvironmentRegister;
+        for (unsigned i = m_symbolTableStack.size(); i--; ) {
+            SymbolTableStackEntry& stackEntry = m_symbolTableStack[i];
+            // We should not resolve a variable to VarKind::Scope if a "with" or "catch" scope lies in between the current
+            // scope and the resolved scope.
+            // We'd like to say: RELEASE_ASSERT(!stackEntry.m_isWithOrCatch);
+            // But, our current implementation 'const' is a pile of crap and uses variablePerSymbolTable
+            // which recklessly ignores the scope stack.
+            // FIXME: When implementing const the proper way ensure we add this assert in.
+            // https://bugs.webkit.org/show_bug.cgi?id=143654
+            if (stackEntry.m_isWithOrCatch)
+                continue;
+
+            if (stackEntry.m_symbolTable->get(variable.ident().impl()).isNull())
+                continue;
+            
+            RegisterID* scope = stackEntry.m_scope; 
+            RELEASE_ASSERT(scope);
+            return scope;
+        }
+
+        RELEASE_ASSERT_NOT_REACHED();
+        return nullptr;
         
     case VarKind::Invalid:
         // Indicates non-local resolution.
@@ -1358,13 +1626,13 @@ RegisterID* BytecodeGenerator::emitResolveScope(RegisterID* dst, const Variable&
         m_codeBlock->addPropertyAccessInstruction(instructions().size());
         
         // resolve_scope dst, id, ResolveType, depth
-        emitOpcode(op_resolve_scope);
         dst = tempDestination(dst);
+        emitOpcode(op_resolve_scope);
         instructions().append(kill(dst));
         instructions().append(scopeRegister()->index());
         instructions().append(addConstant(variable.ident()));
         instructions().append(resolveType());
-        instructions().append(0);
+        instructions().append(currentScopeDepth());
         instructions().append(0);
         return dst;
     }
@@ -1398,7 +1666,7 @@ RegisterID* BytecodeGenerator::emitGetFromScope(RegisterID* dst, RegisterID* sco
         instructions().append(scope->index());
         instructions().append(addConstant(variable.ident()));
         instructions().append(ResolveModeAndType(resolveMode, variable.offset().isScope() ? LocalClosureVar : resolveType()).operand());
-        instructions().append(0);
+        instructions().append(currentScopeDepth());
         instructions().append(variable.offset().isScope() ? variable.offset().scopeOffset().offset() : 0);
         instructions().append(profile);
         return dst;
@@ -1434,11 +1702,12 @@ RegisterID* BytecodeGenerator::emitPutToScope(RegisterID* scope, const Variable&
         if (variable.offset().isScope()) {
             offset = variable.offset().scopeOffset();
             instructions().append(ResolveModeAndType(resolveMode, LocalClosureVar).operand());
+            instructions().append(variable.symbolTableConstantIndex());
         } else {
             ASSERT(resolveType() != LocalClosureVar);
             instructions().append(ResolveModeAndType(resolveMode, resolveType()).operand());
+            instructions().append(currentScopeDepth());
         }
-        instructions().append(0);
         instructions().append(!!offset ? offset.offset() : 0);
         return value;
     } }
@@ -1448,26 +1717,8 @@ RegisterID* BytecodeGenerator::emitPutToScope(RegisterID* scope, const Variable&
 
 RegisterID* BytecodeGenerator::initializeVariable(const Variable& variable, RegisterID* value)
 {
-    RegisterID* scope;
-    switch (variable.offset().kind()) {
-    case VarKind::Stack:
-        scope = nullptr;
-        break;
-        
-    case VarKind::DirectArgument:
-        scope = argumentsRegister();
-        break;
-        
-    case VarKind::Scope:
-        scope = scopeRegister();
-        break;
-        
-    default:
-        scope = nullptr;
-        RELEASE_ASSERT_NOT_REACHED();
-        break;
-    }
-
+    RELEASE_ASSERT(variable.offset().kind() != VarKind::Invalid);
+    RegisterID* scope = emitResolveScope(nullptr, variable);
     return emitPutToScope(scope, variable, value, ThrowIfNotFound);
 }
 
@@ -1691,6 +1942,55 @@ void BytecodeGenerator::emitTDZCheck(RegisterID* target)
     instructions().append(target->index());
 }
 
+bool BytecodeGenerator::needsTDZCheck(const Variable& variable)
+{
+    for (unsigned i = m_TDZStack.size(); i--;) {
+        VariableEnvironment& identifiers = m_TDZStack[i].first;
+        if (identifiers.contains(variable.ident().impl()))
+            return true;
+    }
+
+    return false;
+}
+
+void BytecodeGenerator::emitTDZCheckIfNecessary(const Variable& variable, RegisterID* target, RegisterID* scope)
+{
+    if (needsTDZCheck(variable)) {
+        if (target)
+            emitTDZCheck(target);
+        else {
+            RELEASE_ASSERT(!variable.isLocal() && scope);
+            RefPtr<RegisterID> result = emitGetFromScope(newTemporary(), scope, variable, DoNotThrowIfNotFound);
+            emitTDZCheck(result.get());
+        }
+    }
+}
+
+void BytecodeGenerator::liftTDZCheckIfPossible(const Variable& variable)
+{
+    RefPtr<UniquedStringImpl> identifier(variable.ident().impl());
+    for (unsigned i = m_TDZStack.size(); i--;) {
+        VariableEnvironment& environment = m_TDZStack[i].first;
+        if (environment.contains(identifier)) {
+            bool isSyntacticallyAbleToOptimizeTDZ = m_TDZStack[i].second;
+            if (isSyntacticallyAbleToOptimizeTDZ) {
+                bool wasRemoved = environment.remove(identifier);
+                RELEASE_ASSERT(wasRemoved);
+            }
+            break;
+        }
+    }
+}
+
+void BytecodeGenerator::getVariablesUnderTDZ(VariableEnvironment& result)
+{
+    for (auto& pair : m_TDZStack) {
+        VariableEnvironment& environment = pair.first;
+        for (auto entry : environment)
+            result.add(entry.key.get());
+    }
+}
+
 RegisterID* BytecodeGenerator::emitNewObject(RegisterID* dst)
 {
     size_t begin = instructions().size();
@@ -2179,7 +2479,17 @@ RegisterID* BytecodeGenerator::emitPushWithScope(RegisterID* dst, RegisterID* sc
     m_scopeContextStack.append(context);
     m_localScopeDepth++;
 
-    return emitUnaryOp(op_push_with_scope, dst, scope);
+    RegisterID* result = emitUnaryOp(op_push_with_scope, dst, scope);
+    m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(), nullptr, true, 0 });
+    return result;
+}
+
+RegisterID* BytecodeGenerator::emitGetParentScope(RegisterID* dst, RegisterID* scope)
+{
+    emitOpcode(op_get_parent_scope);
+    instructions().append(dst->index());
+    instructions().append(scope->index());
+    return dst;
 }
 
 void BytecodeGenerator::emitPopScope(RegisterID* srcDst)
@@ -2187,11 +2497,13 @@ void BytecodeGenerator::emitPopScope(RegisterID* srcDst)
     ASSERT(m_scopeContextStack.size());
     ASSERT(!m_scopeContextStack.last().isFinallyBlock);
 
-    emitOpcode(op_pop_scope);
-    instructions().append(srcDst->index());
+    RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), srcDst);
+    emitMove(srcDst, parentScope.get());
 
     m_scopeContextStack.removeLast();
     m_localScopeDepth--;
+    SymbolTableStackEntry stackEntry = m_symbolTableStack.takeLast();
+    RELEASE_ASSERT(stackEntry.m_isWithOrCatch);
 }
 
 void BytecodeGenerator::emitDebugHook(DebugHookID debugHookID, unsigned line, unsigned charOffset, unsigned lineStart)
@@ -2227,6 +2539,7 @@ void BytecodeGenerator::pushFinallyContext(StatementNode* finallyBlock)
         static_cast<unsigned>(m_forInContextStack.size()),
         static_cast<unsigned>(m_tryContextStack.size()),
         static_cast<unsigned>(m_labelScopes.size()),
+        static_cast<unsigned>(m_symbolTableStack.size()),
         m_finallyDepth,
         m_localScopeDepth
     };
@@ -2252,6 +2565,7 @@ void BytecodeGenerator::pushIteratorCloseContext(RegisterID* iterator, Throwable
         static_cast<unsigned>(m_forInContextStack.size()),
         static_cast<unsigned>(m_tryContextStack.size()),
         static_cast<unsigned>(m_labelScopes.size()),
+        static_cast<unsigned>(m_symbolTableStack.size()),
         m_finallyDepth,
         m_localScopeDepth
     };
@@ -2385,9 +2699,10 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte
         if (nNormalScopes) {
             // We need to remove a number of dynamic scopes to get to the next
             // finally block
+            RefPtr<RegisterID> parentScope = newTemporary();
             while (nNormalScopes--) {
-                emitOpcode(op_pop_scope);
-                instructions().append(scope->index());
+                parentScope = emitGetParentScope(parentScope.get(), scope);
+                emitMove(scope, parentScope.get());
             }
 
             // If topScope == bottomScope then there isn't a finally block left to emit.
@@ -2399,6 +2714,7 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte
         Vector<SwitchInfo> savedSwitchContextStack;
         Vector<std::unique_ptr<ForInContext>> savedForInContextStack;
         Vector<TryContext> poppedTryContexts;
+        Vector<SymbolTableStackEntry> savedSymbolTableStack;
         LabelScopeStore savedLabelScopes;
         while (topScope > bottomScope && topScope->isFinallyBlock) {
             RefPtr<Label> beforeFinally = emitLabel(newLabel().get());
@@ -2411,6 +2727,7 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte
             bool flipForIns = finallyContext.forInContextStackSize != m_forInContextStack.size();
             bool flipTries = finallyContext.tryContextStackSize != m_tryContextStack.size();
             bool flipLabelScopes = finallyContext.labelScopesSize != m_labelScopes.size();
+            bool flipSymbolTableStack = finallyContext.symbolTableStackSize != m_symbolTableStack.size();
             int topScopeIndex = -1;
             int bottomScopeIndex = -1;
             if (flipScopes) {
@@ -2445,6 +2762,10 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte
                 while (m_labelScopes.size() > finallyContext.labelScopesSize)
                     m_labelScopes.removeLast();
             }
+            if (flipSymbolTableStack) {
+                savedSymbolTableStack = m_symbolTableStack;
+                m_symbolTableStack.shrink(finallyContext.symbolTableStackSize);
+            }
             int savedFinallyDepth = m_finallyDepth;
             m_finallyDepth = finallyContext.finallyDepth;
             int savedDynamicScopeDepth = m_localScopeDepth;
@@ -2482,6 +2803,8 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte
             }
             if (flipLabelScopes)
                 m_labelScopes = savedLabelScopes;
+            if (flipSymbolTableStack)
+                m_symbolTableStack = savedSymbolTableStack;
             m_finallyDepth = savedFinallyDepth;
             m_localScopeDepth = savedDynamicScopeDepth;
             
@@ -2500,9 +2823,10 @@ void BytecodeGenerator::emitPopScopes(RegisterID* scope, int targetScopeDepth)
         return;
 
     if (!m_finallyDepth) {
+        RefPtr<RegisterID> parentScope = newTemporary();
         while (scopeDelta--) {
-            emitOpcode(op_pop_scope);
-            instructions().append(scope->index());
+            parentScope = emitGetParentScope(parentScope.get(), scope);
+            emitMove(scope, parentScope.get());
         }
         return;
     }
@@ -2542,7 +2866,7 @@ void BytecodeGenerator::popTryAndEmitCatch(TryData* tryData, RegisterID* excepti
     m_tryContextStack.removeLast();
     
     emitLabel(tryRange.tryData->target.get());
-    tryRange.tryData->targetScopeDepth = m_localScopeDepth;
+    tryRange.tryData->targetScopeDepth = calculateTargetScopeDepthForExceptionHandler();
     tryRange.tryData->handlerType = handlerType;
 
     emitOpcode(op_catch);
@@ -2550,6 +2874,39 @@ void BytecodeGenerator::popTryAndEmitCatch(TryData* tryData, RegisterID* excepti
     instructions().append(thrownValueRegister->index());
 }
 
+int BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler() const
+{
+    int depth = m_localScopeDepth;
+
+    for (unsigned i = m_symbolTableStack.size(); i--; ) {
+        RegisterID* scope = m_symbolTableStack[i].m_scope;
+        if (scope)
+            depth++;
+    }
+
+    // Currently, we're maintaing compatibility with how things are done and letting the exception handling
+    // code take into consideration the base activation of the function. There is no reason we shouldn't 
+    // be able to calculate the exact depth here and let the exception handler not worry if there is a base
+    // activation or not.
+    if (m_lexicalEnvironmentRegister)
+        depth--;
+
+    ASSERT(depth >= 0);
+    return depth;
+}
+
+int BytecodeGenerator::currentScopeDepth() const
+{
+    // This is the current number of JSScope descendents that would be allocated
+    // in this function/program if this code were running.
+    int depth = 0;
+    for (unsigned i = m_symbolTableStack.size(); i--; ) {
+        if (m_symbolTableStack[i].m_scope || m_symbolTableStack[i].m_isWithOrCatch)
+            depth++;
+    }
+    return depth;
+}
+
 void BytecodeGenerator::emitThrowReferenceError(const String& message)
 {
     emitOpcode(op_throw_static_error);
@@ -2585,6 +2942,8 @@ void BytecodeGenerator::emitPushCatchScope(RegisterID* dst, const Identifier& pr
     instructions().append(value->index());
     instructions().append(addConstantValue(SymbolTable::createNameScopeTable(*vm(), property, attributes))->index());
     instructions().append(JSNameScope::CatchScope);
+
+    m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(), nullptr, true, 0 });
 }
 
 void BytecodeGenerator::beginSwitch(RegisterID* scrutineeRegister, SwitchInfo::SwitchType type)
@@ -2726,7 +3085,7 @@ void BytecodeGenerator::emitReadOnlyExceptionIfNeeded()
     instructions().append(false);
 }
     
-void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack)
+void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack, VariableEnvironmentNode* forLoopNode, RegisterID* forLoopSymbolTable)
 {
     RefPtr<RegisterID> subject = newTemporary();
     emitNode(subject.get(), subjectNode);
@@ -2788,6 +3147,9 @@ void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, Expressio
         }
 
         emitLabel(scope->continueTarget());
+        if (forLoopNode)
+            prepareLexicalScopeForNextForLoopIteration(forLoopNode, forLoopSymbolTable);
+
         {
             emitIteratorNext(value.get(), iterator.get(), node);
             emitJumpIfTrue(emitGetById(newTemporary(), value.get(), propertyNames().done), loopDone.get());
index 1a9771e..598a37d 100644 (file)
@@ -91,6 +91,7 @@ namespace JSC {
         unsigned forInContextStackSize;
         unsigned tryContextStackSize;
         unsigned labelScopesSize;
+        unsigned symbolTableStackSize;
         int finallyDepth;
         int dynamicScopeDepth;
     };
@@ -192,6 +193,7 @@ namespace JSC {
             , m_local(nullptr)
             , m_attributes(0)
             , m_kind(NormalVariable)
+            , m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable.
         {
         }
         
@@ -200,22 +202,25 @@ namespace JSC {
             , m_local(nullptr)
             , m_attributes(0)
             , m_kind(NormalVariable) // This is somewhat meaningless here for this kind of Variable.
+            , m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable.
         {
         }
 
-        Variable(const Identifier& ident, VarOffset offset, RegisterID* local, unsigned attributes, VariableKind kind)
+        Variable(const Identifier& ident, VarOffset offset, RegisterID* local, unsigned attributes, VariableKind kind, int symbolTableConstantIndex)
             : m_ident(ident)
             , m_offset(offset)
             , m_local(local)
             , m_attributes(attributes)
             , m_kind(kind)
+            , m_symbolTableConstantIndex(symbolTableConstantIndex)
         {
         }
 
         // If it's unset, then it is a non-locally-scoped variable. If it is set, then it could be
-        // a stack variable, a scoped variable in the local scope, or a variable captured in the
+        // a stack variable, a scoped variable in a local scope, or a variable captured in the
         // direct arguments object.
         bool isResolved() const { return !!m_offset; }
+        int symbolTableConstantIndex() const { ASSERT(isResolved() && !isSpecial()); return m_symbolTableConstantIndex; }
         
         const Identifier& ident() const { return m_ident; }
         
@@ -232,6 +237,7 @@ namespace JSC {
         RegisterID* m_local;
         unsigned m_attributes;
         VariableKind m_kind;
+        int m_symbolTableConstantIndex;
     };
 
     struct TryRange {
@@ -255,12 +261,11 @@ namespace JSC {
         WTF_MAKE_FAST_ALLOCATED;
         WTF_MAKE_NONCOPYABLE(BytecodeGenerator);
     public:
-        typedef DeclarationStacks::VarStack VarStack;
         typedef DeclarationStacks::FunctionStack FunctionStack;
 
-        BytecodeGenerator(VM&, ProgramNode*, UnlinkedProgramCodeBlock*, DebuggerMode, ProfilerMode);
-        BytecodeGenerator(VM&, FunctionNode*, UnlinkedFunctionCodeBlock*, DebuggerMode, ProfilerMode);
-        BytecodeGenerator(VM&, EvalNode*, UnlinkedEvalCodeBlock*, DebuggerMode, ProfilerMode);
+        BytecodeGenerator(VM&, ProgramNode*, UnlinkedProgramCodeBlock*, DebuggerMode, ProfilerMode, const VariableEnvironment*);
+        BytecodeGenerator(VM&, FunctionNode*, UnlinkedFunctionCodeBlock*, DebuggerMode, ProfilerMode, const VariableEnvironment*);
+        BytecodeGenerator(VM&, EvalNode*, UnlinkedEvalCodeBlock*, DebuggerMode, ProfilerMode, const VariableEnvironment*);
 
         ~BytecodeGenerator();
         
@@ -310,6 +315,13 @@ namespace JSC {
 
         RegisterID* ignoredResult() { return &m_ignoredResultRegister; }
 
+        // This will be allocated in the temporary region of registers, but it will
+        // not be marked as a temporary. This will ensure that finalDestination() does
+        // not overwrite a block scope variable that it mistakes as a temporary. These
+        // registers can be (and are) reclaimed when the lexical scope they belong to
+        // is no longer on the symbol table stack.
+        RegisterID* newBlockScopeVariable();
+
         // Returns a place to write intermediate values of an operation
         // which reuses dst if it is safe to do so.
         RegisterID* tempDestination(RegisterID* dst)
@@ -450,6 +462,9 @@ namespace JSC {
 
         RegisterID* emitCreateThis(RegisterID* dst);
         void emitTDZCheck(RegisterID* target);
+        bool needsTDZCheck(const Variable&);
+        void emitTDZCheckIfNecessary(const Variable&, RegisterID* target, RegisterID* scope);
+        void liftTDZCheckIfPossible(const Variable&);
         RegisterID* emitNewObject(RegisterID* dst);
         RegisterID* emitNewArray(RegisterID* dst, ElementNode*, unsigned length); // stops at first elision
 
@@ -503,7 +518,7 @@ namespace JSC {
         void emitCallDefineProperty(RegisterID* newObj, RegisterID* propertyNameRegister,
             RegisterID* valueRegister, RegisterID* getterRegister, RegisterID* setterRegister, unsigned options, const JSTextPosition&);
 
-        void emitEnumeration(ThrowableExpressionData* enumerationNode, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack);
+        void emitEnumeration(ThrowableExpressionData* enumerationNode, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack, VariableEnvironmentNode* = nullptr, RegisterID* forLoopSymbolTable = nullptr);
 
 #if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX)
         RegisterID* emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode*);
@@ -571,6 +586,7 @@ namespace JSC {
         void emitGetScope();
         RegisterID* emitPushWithScope(RegisterID* dst, RegisterID* scope);
         void emitPopScope(RegisterID* srcDst);
+        RegisterID* emitGetParentScope(RegisterID* dst, RegisterID* scope);
 
         void emitDebugHook(DebugHookID, unsigned line, unsigned charOffset, unsigned lineStart);
 
@@ -605,8 +621,13 @@ namespace JSC {
 
         OpcodeID lastOpcodeID() const { return m_lastOpcodeID; }
 
+        void pushLexicalScope(VariableEnvironmentNode*, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult = nullptr);
+        void popLexicalScope(VariableEnvironmentNode*);
+        void prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode*, RegisterID* loopSymbolTable);
+
     private:
-        Variable variableForLocalEntry(const Identifier&, const SymbolTableEntry&);
+        void reclaimFreeRegisters();
+        Variable variableForLocalEntry(const Identifier&, const SymbolTableEntry&, int);
 
         void emitOpcode(OpcodeID);
         UnlinkedArrayAllocationProfile newArrayAllocationProfile();
@@ -677,9 +698,13 @@ namespace JSC {
         
         UnlinkedFunctionExecutable* makeFunction(FunctionBodyNode* body)
         {
-            return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), body, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction);
+            VariableEnvironment variablesUnderTDZ;
+            getVariablesUnderTDZ(variablesUnderTDZ);
+            return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), body, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, variablesUnderTDZ);
         }
 
+        void getVariablesUnderTDZ(VariableEnvironment&);
+
         RegisterID* emitConstructVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
         RegisterID* emitCallVarargs(OpcodeID, RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
 
@@ -691,31 +716,6 @@ namespace JSC {
 
         SymbolTable& symbolTable() { return *m_symbolTable; }
 
-        bool shouldOptimizeLocals()
-        {
-            if (m_codeType != FunctionCode)
-                return false;
-
-            if (m_localScopeDepth)
-                return false;
-
-            return true;
-        }
-
-        bool canOptimizeNonLocals()
-        {
-            if (m_localScopeDepth)
-                return false;
-
-            if (m_codeType == EvalCode)
-                return false;
-
-            if (m_codeType == FunctionCode && m_codeBlock->usesEval())
-                return false;
-
-            return true;
-        }
-
         RegisterID* emitThrowExpressionTooDeepException();
 
     private:
@@ -725,6 +725,14 @@ namespace JSC {
         bool m_shouldEmitProfileHooks;
 
         SymbolTable* m_symbolTable { nullptr };
+        struct SymbolTableStackEntry {
+            Strong<SymbolTable> m_symbolTable;
+            RegisterID* m_scope;
+            bool m_isWithOrCatch;
+            int m_symbolTableConstantIndex;
+        };
+        Vector<SymbolTableStackEntry> m_symbolTableStack;
+        Vector<std::pair<VariableEnvironment, bool>> m_TDZStack;
 
         ScopeNode* const m_scopeNode;
         Strong<UnlinkedCodeBlock> m_codeBlock;
@@ -752,6 +760,9 @@ namespace JSC {
         int m_localScopeDepth { 0 };
         const CodeType m_codeType;
 
+        int calculateTargetScopeDepthForExceptionHandler() const;
+        int currentScopeDepth() const;
+
         Vector<ControlFlowContext, 0, UnsafeVectorOverflow> m_scopeContextStack;
         Vector<SwitchInfo> m_switchContextStack;
         Vector<std::unique_ptr<ForInContext>> m_forInContextStack;
index cfd3ef1..6cb5486 100644 (file)
@@ -196,6 +196,7 @@ RegisterID* ResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID*
 {
     Variable var = generator.variable(m_ident);
     if (RegisterID* local = var.local()) {
+        generator.emitTDZCheckIfNecessary(var, local, nullptr);
         if (dst == generator.ignoredResult())
             return nullptr;
         if (generator.vm()->typeProfiler()) {
@@ -210,6 +211,7 @@ RegisterID* ResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID*
     RefPtr<RegisterID> scope = generator.emitResolveScope(dst, var);
     RegisterID* finalDest = generator.finalDestination(dst);
     RegisterID* result = generator.emitGetFromScope(finalDest, scope.get(), var, ThrowIfNotFound);
+    generator.emitTDZCheckIfNecessary(var, finalDest, nullptr);
     if (generator.vm()->typeProfiler()) {
         generator.emitProfileType(finalDest, var.isResolved() ? ProfileTypeBytecodeGetFromLocalScope : ProfileTypeBytecodeGetFromScope, &m_ident);
         generator.emitTypeProfilerExpressionInfo(m_position, JSTextPosition(-1, m_position.offset + m_ident.length(), -1));
@@ -737,6 +739,7 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator,
 
     Variable var = generator.variable(m_ident);
     if (RegisterID* local = var.local()) {
+        generator.emitTDZCheckIfNecessary(var, local, nullptr);
         RefPtr<RegisterID> func = generator.emitMove(generator.tempDestination(dst), local);
         RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get());
         CallArguments callArguments(generator, m_args);
@@ -761,6 +764,7 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator,
         callArguments.thisRegister(),
         generator.emitResolveScope(callArguments.thisRegister(), var));
     generator.emitGetFromScope(func.get(), callArguments.thisRegister(), var, ThrowIfNotFound);
+    generator.emitTDZCheckIfNecessary(var, func.get(), nullptr);
     RegisterID* ret = generator.emitCall(returnValue.get(), func.get(), expectedFunction, callArguments, divot(), divotStart(), divotEnd());
     if (generator.vm()->typeProfiler()) {
         generator.emitProfileType(returnValue.get(), ProfileTypeBytecodeDoesNotHaveGlobalID, nullptr);
@@ -1058,6 +1062,7 @@ RegisterID* PostfixNode::emitResolve(BytecodeGenerator& generator, RegisterID* d
 
     Variable var = generator.variable(ident);
     if (RegisterID* local = var.local()) {
+        generator.emitTDZCheckIfNecessary(var, local, nullptr);
         RefPtr<RegisterID> localReg = local;
         if (var.isReadOnly()) {
             generator.emitReadOnlyExceptionIfNeeded();
@@ -1080,6 +1085,7 @@ RegisterID* PostfixNode::emitResolve(BytecodeGenerator& generator, RegisterID* d
     generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
     RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
     RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound);
+    generator.emitTDZCheckIfNecessary(var, value.get(), nullptr);
     RefPtr<RegisterID> oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator);
     generator.emitPutToScope(scope.get(), var, value.get(), ThrowIfNotFound);
     if (generator.vm()->typeProfiler()) {
@@ -1160,11 +1166,14 @@ RegisterID* PostfixNode::emitBytecode(BytecodeGenerator& generator, RegisterID*
 RegisterID* DeleteResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
     Variable var = generator.variable(m_ident);
-    if (var.local())
+    if (var.local()) {
+        generator.emitTDZCheckIfNecessary(var, var.local(), nullptr);
         return generator.emitLoad(generator.finalDestination(dst), false);
+    }
 
     generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
     RefPtr<RegisterID> base = generator.emitResolveScope(dst, var);
+    generator.emitTDZCheckIfNecessary(var, nullptr, base.get());
     return generator.emitDeleteById(generator.finalDestination(dst, base.get()), base.get(), m_ident);
 }
 
@@ -1221,6 +1230,7 @@ RegisterID* TypeOfResolveNode::emitBytecode(BytecodeGenerator& generator, Regist
 {
     Variable var = generator.variable(m_ident);
     if (RegisterID* local = var.local()) {
+        generator.emitTDZCheckIfNecessary(var, local, nullptr);
         if (dst == generator.ignoredResult())
             return 0;
         return generator.emitTypeOf(generator.finalDestination(dst), local);
@@ -1228,6 +1238,7 @@ RegisterID* TypeOfResolveNode::emitBytecode(BytecodeGenerator& generator, Regist
 
     RefPtr<RegisterID> scope = generator.emitResolveScope(dst, var);
     RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, DoNotThrowIfNotFound);
+    generator.emitTDZCheckIfNecessary(var, value.get(), nullptr);
     if (dst == generator.ignoredResult())
         return 0;
     return generator.emitTypeOf(generator.finalDestination(dst, scope.get()), value.get());
@@ -1255,6 +1266,7 @@ RegisterID* PrefixNode::emitResolve(BytecodeGenerator& generator, RegisterID* ds
 
     Variable var = generator.variable(ident);
     if (RegisterID* local = var.local()) {
+        generator.emitTDZCheckIfNecessary(var, local, nullptr);
         RefPtr<RegisterID> localReg = local;
         if (var.isReadOnly()) {
             generator.emitReadOnlyExceptionIfNeeded();
@@ -1275,6 +1287,8 @@ RegisterID* PrefixNode::emitResolve(BytecodeGenerator& generator, RegisterID* ds
     generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
     RefPtr<RegisterID> scope = generator.emitResolveScope(dst, var);
     RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound);
+    generator.emitTDZCheckIfNecessary(var, value.get(), nullptr);
+
     emitIncOrDec(generator, value.get(), m_operator);
     generator.emitPutToScope(scope.get(), var, value.get(), ThrowIfNotFound);
     if (generator.vm()->typeProfiler()) {
@@ -1772,6 +1786,7 @@ RegisterID* ReadModifyResolveNode::emitBytecode(BytecodeGenerator& generator, Re
     JSTextPosition newDivot = divotStart() + m_ident.length();
     Variable var = generator.variable(m_ident);
     if (RegisterID* local = var.local()) {
+        generator.emitTDZCheckIfNecessary(var, local, nullptr);
         if (var.isReadOnly()) {
             generator.emitReadOnlyExceptionIfNeeded();
             return emitReadModifyAssignment(generator, generator.finalDestination(dst), local, m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor()));
@@ -1797,6 +1812,7 @@ RegisterID* ReadModifyResolveNode::emitBytecode(BytecodeGenerator& generator, Re
     generator.emitExpressionInfo(newDivot, divotStart(), newDivot);
     RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
     RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound);
+    generator.emitTDZCheckIfNecessary(var, value.get(), nullptr);
     RefPtr<RegisterID> result = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor()), this);
     RegisterID* returnResult = generator.emitPutToScope(scope.get(), var, result.get(), ThrowIfNotFound);
     if (generator.vm()->typeProfiler()) {
@@ -1812,27 +1828,36 @@ RegisterID* AssignResolveNode::emitBytecode(BytecodeGenerator& generator, Regist
 {
     Variable var = generator.variable(m_ident);
     if (RegisterID* local = var.local()) {
+        RegisterID* result = nullptr;
+        if (m_assignmentContext == AssignmentContext::AssignmentExpression)
+            generator.emitTDZCheckIfNecessary(var, local, nullptr);
         if (var.isReadOnly()) {
             generator.emitReadOnlyExceptionIfNeeded();
-            return generator.emitNode(dst, m_right);
-        }
-        if (var.isSpecial() || generator.vm()->typeProfiler()) {
+            result = generator.emitNode(dst, m_right);
+        } else if (var.isSpecial() || generator.vm()->typeProfiler()) {
             RefPtr<RegisterID> tempDst = generator.tempDestination(dst);
             generator.emitNode(tempDst.get(), m_right);
             generator.emitMove(local, tempDst.get());
             generator.invalidateForInContextForLocal(local);
             if (generator.vm()->typeProfiler())
                 generator.emitTypeProfilerExpressionInfo(divotStart(), divotEnd());
-            return generator.moveToDestinationIfNeeded(dst, tempDst.get());
+            result = generator.moveToDestinationIfNeeded(dst, tempDst.get());
+        } else {
+            RegisterID* right = generator.emitNode(local, m_right);
+            generator.invalidateForInContextForLocal(local);
+            result = generator.moveToDestinationIfNeeded(dst, right);
         }
-        RegisterID* result = generator.emitNode(local, m_right);
-        generator.invalidateForInContextForLocal(local);
-        return generator.moveToDestinationIfNeeded(dst, result);
+
+        if (m_assignmentContext == AssignmentContext::DeclarationStatement)
+            generator.liftTDZCheckIfPossible(var);
+        return result;
     }
 
     if (generator.isStrictMode())
         generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
     RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+    if (m_assignmentContext == AssignmentContext::AssignmentExpression)
+        generator.emitTDZCheckIfNecessary(var, nullptr, scope.get());
     if (dst == generator.ignoredResult())
         dst = 0;
     RefPtr<RegisterID> result = generator.emitNode(dst, m_right);
@@ -1841,7 +1866,10 @@ RegisterID* AssignResolveNode::emitBytecode(BytecodeGenerator& generator, Regist
     if (generator.vm()->typeProfiler()) {
         generator.emitProfileType(result.get(), var.isResolved() ? ProfileTypeBytecodePutToLocalScope : ProfileTypeBytecodePutToScope, &m_ident);
         generator.emitTypeProfilerExpressionInfo(divotStart(), divotEnd());
-    } 
+    }
+
+    if (m_assignmentContext == AssignmentContext::DeclarationStatement)
+        generator.liftTDZCheckIfPossible(var);
     return returnResult;
 }
 
@@ -2035,7 +2063,9 @@ void BlockNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
     if (!m_statements)
         return;
+    generator.pushLexicalScope(this, true);
     m_statements->emitBytecode(generator, dst);
+    generator.popLexicalScope(this);
 }
 
 // ------------------------------ EmptyStatementNode ---------------------------
@@ -2061,9 +2091,9 @@ void ExprStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* d
     generator.emitNode(dst, m_expr);
 }
 
-// ------------------------------ VarStatementNode ----------------------------
+// ------------------------------ DeclarationStatement ----------------------------
 
-void VarStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
+void DeclarationStatement::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 {
     ASSERT(m_expr);
     generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
@@ -2074,6 +2104,7 @@ void VarStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 
 RegisterID* EmptyVarExpression::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 {
+    // It's safe to return null here because this node will always be a child node of DeclarationStatement which ignores our return value.
     if (!generator.vm()->typeProfiler())
         return nullptr;
 
@@ -2088,7 +2119,31 @@ RegisterID* EmptyVarExpression::emitBytecode(BytecodeGenerator& generator, Regis
 
     generator.emitTypeProfilerExpressionInfo(position(), JSTextPosition(-1, position().offset + m_ident.length(), -1));
 
-    // It's safe to return null here because this node will always be a child node of VarStatementNode which ignores our return value.
+    return nullptr;
+}
+
+// ------------------------------ EmptyLetExpression ----------------------------
+
+RegisterID* EmptyLetExpression::emitBytecode(BytecodeGenerator& generator, RegisterID*)
+{
+    // Lexical declarations like 'let' must move undefined into their variables so we don't
+    // get TDZ errors for situations like this: `let x; x;`
+    Variable var = generator.variable(m_ident);
+    if (RegisterID* local = var.local()) {
+        generator.emitLoad(local, jsUndefined());
+        if (generator.vm()->typeProfiler())
+            generator.emitProfileType(local, ProfileTypeBytecodeHasGlobalID, nullptr);
+    } else {
+        RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+        RefPtr<RegisterID> value = generator.emitLoad(nullptr, jsUndefined());
+        generator.emitPutToScope(scope.get(), var, value.get(), generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound);
+        if (generator.vm()->typeProfiler())
+            generator.emitProfileType(value.get(), var.isResolved() ? ProfileTypeBytecodeGetFromLocalScope : ProfileTypeBytecodeGetFromScope, &m_ident);
+    }
+    if (generator.vm()->typeProfiler())
+        generator.emitTypeProfilerExpressionInfo(position(), JSTextPosition(-1, position().offset + m_ident.length(), -1));
+
+    // It's safe to return null here because this node will always be a child node of DeclarationStatement which ignores our return value.
     return nullptr;
 }
 
@@ -2218,6 +2273,9 @@ void ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
     LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop);
 
+    RegisterID* forLoopSymbolTable = nullptr;
+    generator.pushLexicalScope(this, true, &forLoopSymbolTable);
+
     generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
 
     if (m_expr1)
@@ -2235,6 +2293,7 @@ void ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 
     generator.emitLabel(scope->continueTarget());
     generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
+    generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable);
     if (m_expr3)
         generator.emitNode(generator.ignoredResult(), m_expr3);
 
@@ -2244,6 +2303,7 @@ void ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
         generator.emitJump(topOfLoop.get());
 
     generator.emitLabel(scope->breakTarget());
+    generator.popLexicalScope(this);
     generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0));
 }
 
@@ -2351,6 +2411,9 @@ void ForInNode::emitMultiLoopBytecode(BytecodeGenerator& generator, RegisterID*
 
     RefPtr<Label> end = generator.newLabel();
 
+    RegisterID* forLoopSymbolTable = nullptr;
+    generator.pushLexicalScope(this, true, &forLoopSymbolTable);
+
     generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
 
     RefPtr<RegisterID> base = generator.newTemporary();
@@ -2395,6 +2458,7 @@ void ForInNode::emitMultiLoopBytecode(BytecodeGenerator& generator, RegisterID*
         generator.emitProfileControlFlow(profilerEndOffset);
 
         generator.emitLabel(scope->continueTarget());
+        generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable);
         generator.emitInc(i.get());
         generator.emitJump(loopStart.get());
 
@@ -2432,6 +2496,7 @@ void ForInNode::emitMultiLoopBytecode(BytecodeGenerator& generator, RegisterID*
         generator.emitProfileControlFlow(profilerEndOffset);
 
         generator.emitLabel(scope->continueTarget());
+        generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable);
         generator.emitInc(enumeratorIndex.get());
         generator.emitEnumeratorStructurePropertyName(propertyName.get(), enumerator.get(), enumeratorIndex.get());
         generator.emitJump(loopStart.get());
@@ -2467,6 +2532,7 @@ void ForInNode::emitMultiLoopBytecode(BytecodeGenerator& generator, RegisterID*
         generator.emitNode(dst, m_statement);
 
         generator.emitLabel(scope->continueTarget());
+        generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable);
         generator.emitInc(enumeratorIndex.get());
         generator.emitEnumeratorGenericPropertyName(propertyName.get(), enumerator.get(), enumeratorIndex.get());
         generator.emitJump(loopStart.get());
@@ -2478,6 +2544,7 @@ void ForInNode::emitMultiLoopBytecode(BytecodeGenerator& generator, RegisterID*
 
     generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
     generator.emitLabel(end.get());
+    generator.popLexicalScope(this);
     generator.emitProfileControlFlow(profilerEndOffset);
 }
 
@@ -2494,6 +2561,8 @@ void ForOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
         return;
     }
 
+    RegisterID* forLoopSymbolTable = nullptr;
+    generator.pushLexicalScope(this, true, &forLoopSymbolTable);
     generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
     auto extractor = [this, dst](BytecodeGenerator& generator, RegisterID* value)
     {
@@ -2543,7 +2612,8 @@ void ForOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
         generator.emitProfileControlFlow(m_statement->startOffset());
         generator.emitNode(dst, m_statement);
     };
-    generator.emitEnumeration(this, m_expr, extractor);
+    generator.emitEnumeration(this, m_expr, extractor, this, forLoopSymbolTable);
+    generator.popLexicalScope(this);
     generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0));
 }
 
@@ -2818,7 +2888,10 @@ void SwitchNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
     LabelScopePtr scope = generator.newLabelScope(LabelScope::Switch);
 
     RefPtr<RegisterID> r0 = generator.emitNode(m_expr);
+
+    generator.pushLexicalScope(this, false);
     m_block->emitBytecodeForBlock(generator, r0.get(), dst);
+    generator.popLexicalScope(this);
 
     generator.emitLabel(scope->breakTarget());
     generator.emitProfileControlFlow(endOffset());
@@ -3320,6 +3393,8 @@ void BindingNode::bindValue(BytecodeGenerator& generator, RegisterID* value) con
 {
     Variable var = generator.variable(m_boundProperty);
     if (RegisterID* local = var.local()) {
+        if (m_bindingContext == AssignmentContext::AssignmentExpression)
+            generator.emitTDZCheckIfNecessary(var, local, nullptr);
         if (var.isReadOnly()) {
             generator.emitReadOnlyExceptionIfNeeded();
             return;
@@ -3327,17 +3402,23 @@ void BindingNode::bindValue(BytecodeGenerator& generator, RegisterID* value) con
         generator.emitMove(local, value);
         if (generator.vm()->typeProfiler())
             generator.emitTypeProfilerExpressionInfo(divotStart(), divotEnd());
+        if (m_bindingContext == AssignmentContext::DeclarationStatement)
+            generator.liftTDZCheckIfPossible(var);
         return;
     }
     if (generator.isStrictMode())
         generator.emitExpressionInfo(divotEnd(), divotStart(), divotEnd());
     RegisterID* scope = generator.emitResolveScope(nullptr, var);
     generator.emitExpressionInfo(divotEnd(), divotStart(), divotEnd());
+    if (m_bindingContext == AssignmentContext::AssignmentExpression)
+        generator.emitTDZCheckIfNecessary(var, nullptr, scope);
     generator.emitPutToScope(scope, var, value, generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound);
     if (generator.vm()->typeProfiler()) {
         generator.emitProfileType(value, var.isResolved() ? ProfileTypeBytecodePutToLocalScope : ProfileTypeBytecodePutToScope, &m_boundProperty);
         generator.emitTypeProfilerExpressionInfo(divotStart(), divotEnd());
     }
+    if (m_bindingContext == AssignmentContext::DeclarationStatement)
+        generator.liftTDZCheckIfPossible(var);
     return;
 }
 
index 663fc03..bf093e8 100644 (file)
@@ -192,7 +192,11 @@ JSValue DebuggerCallFrame::evaluate(const String& script, NakedPtr<Exception>& e
     VM& vm = callFrame->vm();
     auto& codeBlock = *callFrame->codeBlock();
     ThisTDZMode thisTDZMode = codeBlock.unlinkedCodeBlock()->constructorKind() == ConstructorKind::Derived ? ThisTDZMode::AlwaysCheck : ThisTDZMode::CheckIfNeeded;
-    EvalExecutable* eval = EvalExecutable::create(callFrame, makeSource(script), codeBlock.isStrictMode(), thisTDZMode);
+
+    VariableEnvironment variablesUnderTDZ;
+    JSScope::collectVariablesUnderTDZ(scope()->jsScope(), variablesUnderTDZ);
+
+    EvalExecutable* eval = EvalExecutable::create(callFrame, makeSource(script), codeBlock.isStrictMode(), thisTDZMode, &variablesUnderTDZ);
     if (vm.exception()) {
         exception = vm.exception();
         vm.clearException();
index d53a6a0..6373223 100644 (file)
@@ -86,7 +86,17 @@ bool DebuggerScope::getOwnPropertySlot(JSObject* object, ExecState* exec, Proper
     // does not presently need to distinguish between what's owned at each level in the
     // prototype chain. Hence, we'll invoke getPropertySlot() on the wrapped scope here
     // instead of getOwnPropertySlot().
-    return thisObject->getPropertySlot(exec, propertyName, slot);
+    bool result = thisObject->getPropertySlot(exec, propertyName, slot);
+    if (result && slot.isValue() && slot.getValue(exec, propertyName) == jsTDZValue()) {
+        // FIXME:
+        // We hit a scope property that has the TDZ empty value.
+        // Currently, we just lie to the inspector and claim that this property is undefined.
+        // This is not ideal and we should fix it.
+        // https://bugs.webkit.org/show_bug.cgi?id=144977
+        slot.setValue(slot.slotBase(), DontEnum, jsUndefined());
+        return true;
+    }
+    return result;
 }
 
 void DebuggerScope::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
index 2e37e69..5fad17b 100644 (file)
@@ -2903,8 +2903,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
         }
 
         case op_check_tdz: {
-            Node* op = get(VirtualRegister(currentInstruction[1].u.operand));
-            addToGraph(CheckNotEmpty, op);
+            addToGraph(CheckNotEmpty, get(VirtualRegister(currentInstruction[1].u.operand)));
             NEXT_OPCODE(op_check_tdz);
         }
 
@@ -3728,13 +3727,25 @@ bool ByteCodeParser::parseBlock(unsigned limit)
         }
             
         case op_create_lexical_environment: {
-            FrozenValue* symbolTable = m_graph.freezeStrong(m_graph.symbolTableFor(currentNodeOrigin().semantic));
-            Node* lexicalEnvironment = addToGraph(CreateActivation, OpInfo(symbolTable), get(VirtualRegister(currentInstruction[2].u.operand)));
+            VirtualRegister symbolTableRegister(currentInstruction[3].u.operand);
+            VirtualRegister initialValueRegister(currentInstruction[4].u.operand);
+            ASSERT(symbolTableRegister.isConstant() && initialValueRegister.isConstant());
+            FrozenValue* symbolTable = m_graph.freezeStrong(m_inlineStackTop->m_codeBlock->getConstant(symbolTableRegister.offset()));
+            FrozenValue* initialValue = m_graph.freezeStrong(m_inlineStackTop->m_codeBlock->getConstant(initialValueRegister.offset()));
+            Node* scope = get(VirtualRegister(currentInstruction[2].u.operand));
+            Node* lexicalEnvironment = addToGraph(CreateActivation, OpInfo(symbolTable), OpInfo(initialValue), scope);
             set(VirtualRegister(currentInstruction[1].u.operand), lexicalEnvironment);
-            set(VirtualRegister(currentInstruction[2].u.operand), lexicalEnvironment);
             NEXT_OPCODE(op_create_lexical_environment);
         }
-            
+
+        case op_get_parent_scope: {
+            Node* currentScope = get(VirtualRegister(currentInstruction[2].u.operand));
+            Node* newScope = addToGraph(SkipScope, currentScope);
+            set(VirtualRegister(currentInstruction[1].u.operand), newScope);
+            addToGraph(Phantom, currentScope);
+            NEXT_OPCODE(op_get_parent_scope);
+        }
+
         case op_get_scope: {
             // Help the later stages a bit by doing some small constant folding here. Note that this
             // only helps for the first basic block. It's extremely important not to constant fold
index ef4c247..bd39381 100644 (file)
@@ -208,6 +208,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_new_func:
     case op_new_func_exp:
     case op_create_lexical_environment:
+    case op_get_parent_scope:
         return CanCompileAndInline;
 
     case op_put_to_scope: {
index 50ba1dd..109a5a6 100644 (file)
@@ -720,6 +720,12 @@ struct Node {
         RELEASE_ASSERT(result);
         return result;
     }
+
+    JSValue initializationValueForActivation() const
+    {
+        ASSERT(op() == CreateActivation);
+        return bitwise_cast<FrozenValue*>(m_opInfo2)->value();
+    }
      
     bool containsMovHint()
     {
index fce6520..41976ad 100644 (file)
@@ -862,11 +862,11 @@ private:
             {
                 SymbolTable* symbolTable = node->castOperand<SymbolTable*>();
                 ConcurrentJITLocker locker(symbolTable->m_lock);
-                LazyNode undefined(m_graph.freeze(jsUndefined()));
+                LazyNode initialValue(m_graph.freeze(node->initializationValueForActivation()));
                 for (auto iter = symbolTable->begin(locker), end = symbolTable->end(locker); iter != end; ++iter) {
                     writes.add(
                         PromotedLocationDescriptor(ClosureVarPLoc, iter->value.scopeOffset().offset()),
-                        undefined);
+                        initialValue);
                 }
             }
             break;
index 6f9512b..99bcffa 100644 (file)
@@ -762,11 +762,13 @@ char* JIT_OPERATION operationNewFloat64ArrayWithOneArgument(
     return newTypedArrayWithOneArgument<JSFloat64Array>(exec, structure, encodedValue);
 }
 
-JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState* exec, Structure* structure, JSScope* scope, SymbolTable* table)
+JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState* exec, Structure* structure, JSScope* scope, SymbolTable* table, EncodedJSValue initialValueEncoded)
 {
+    JSValue initialValue = JSValue::decode(initialValueEncoded);
+    ASSERT(initialValue == jsUndefined() || initialValue == jsTDZValue());
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
-    return JSLexicalEnvironment::create(vm, structure, scope, table);
+    return JSLexicalEnvironment::create(vm, structure, scope, table, initialValue);
 }
 
 JSCell* JIT_OPERATION operationCreateDirectArguments(ExecState* exec, Structure* structure, int32_t length, int32_t minCapacity)
index c102b87..55b4015 100644 (file)
@@ -96,7 +96,7 @@ EncodedJSValue JIT_OPERATION operationRegExpExec(ExecState*, JSCell*, JSCell*) W
 size_t JIT_OPERATION operationRegExpTest(ExecState*, JSCell*, JSCell*) WTF_INTERNAL;
 size_t JIT_OPERATION operationCompareStrictEqCell(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 size_t JIT_OPERATION operationCompareStrictEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState*, Structure*, JSScope*, SymbolTable*);
+JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState*, Structure*, JSScope*, SymbolTable*, EncodedJSValue);
 JSCell* JIT_OPERATION operationCreateDirectArguments(ExecState*, Structure*, int32_t length, int32_t minCapacity);
 JSCell* JIT_OPERATION operationCreateDirectArgumentsDuringExit(ExecState*, InlineCallFrame*, JSFunction*, int32_t argumentCount);
 JSCell* JIT_OPERATION operationCreateScopedArguments(ExecState*, Structure*, Register* argumentStart, int32_t length, JSFunction* callee, JSLexicalEnvironment*);
index 9644995..f3f8af7 100644 (file)
@@ -4699,6 +4699,8 @@ void SpeculativeJIT::compileCreateActivation(Node* node)
         
     SpeculateCellOperand scope(this, node->child1());
     GPRReg scopeGPR = scope.gpr();
+    JSValue initializationValue = node->initializationValueForActivation();
+    ASSERT(initializationValue == jsUndefined() || initializationValue == jsTDZValue());
     
     if (table->singletonScope()->isStillValid()) {
         GPRFlushedCallResult result(this);
@@ -4706,7 +4708,13 @@ void SpeculativeJIT::compileCreateActivation(Node* node)
         
         flushRegisters();
         
-        callOperation(operationCreateActivationDirect, resultGPR, structure, scopeGPR, table);
+#if USE(JSVALUE64)
+        callOperation(operationCreateActivationDirect,
+            resultGPR, structure, scopeGPR, table, TrustedImm64(JSValue::encode(initializationValue)));
+#else
+        callOperation(operationCreateActivationDirect,
+            resultGPR, structure, scopeGPR, table, TrustedImm32(initializationValue.tag()), TrustedImm32(initializationValue.payload()));
+#endif
         cellResult(resultGPR, node);
         return;
     }
@@ -4730,17 +4738,23 @@ void SpeculativeJIT::compileCreateActivation(Node* node)
         TrustedImmPtr(table),
         JITCompiler::Address(resultGPR, JSLexicalEnvironment::offsetOfSymbolTable()));
         
-    // Must initialize all members to undefined.
+    // Must initialize all members to undefined or the TDZ empty value.
     for (unsigned i = 0; i < table->scopeSize(); ++i) {
         m_jit.storeTrustedValue(
-            jsUndefined(),
+            initializationValue,
             JITCompiler::Address(
                 resultGPR, JSLexicalEnvironment::offsetOfVariable(ScopeOffset(i))));
     }
 
+#if USE(JSVALUE64)
     addSlowPathGenerator(
         slowPathCall(
-            slowPath, this, operationCreateActivationDirect, resultGPR, structure, scopeGPR, table));
+            slowPath, this, operationCreateActivationDirect, resultGPR, structure, scopeGPR, table, TrustedImm64(JSValue::encode(initializationValue))));
+#else
+    addSlowPathGenerator(
+        slowPathCall(
+            slowPath, this, operationCreateActivationDirect, resultGPR, structure, scopeGPR, table, TrustedImm32(initializationValue.tag()), TrustedImm32(initializationValue.payload())));
+#endif
 
     cellResult(resultGPR, node);
 }
index 8798b6d..a5c4454 100644 (file)
@@ -1004,11 +1004,20 @@ public:
         m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure));
         return appendCallWithExceptionCheckSetResult(operation, result);
     }
-    JITCompiler::Call callOperation(C_JITOperation_EStJscSymtab operation, GPRReg result, Structure* structure, GPRReg scope, SymbolTable* table)
+
+#if USE(JSVALUE64)
+    JITCompiler::Call callOperation(C_JITOperation_EStJscSymtabJ operation, GPRReg result, Structure* structure, GPRReg scope, SymbolTable* table, TrustedImm64 initialValue)
     {
-        m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure), scope, TrustedImmPtr(table));
+        m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure), scope, TrustedImmPtr(table), initialValue);
         return appendCallWithExceptionCheckSetResult(operation, result);
     }
+#else
+    JITCompiler::Call callOperation(C_JITOperation_EStJscSymtabJ operation, GPRReg result, Structure* structure, GPRReg scope, SymbolTable* table, TrustedImm32 tag, TrustedImm32 payload)
+    {
+        m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure), scope, TrustedImmPtr(table), payload, tag);
+        return appendCallWithExceptionCheckSetResult(operation, result);
+    }
+#endif
     JITCompiler::Call callOperation(C_JITOperation_EStZ operation, GPRReg result, Structure* structure, unsigned knownLength)
     {
         m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure), TrustedImm32(knownLength));
index 3e62d30..9b34414 100644 (file)
@@ -66,7 +66,7 @@ namespace JSC { namespace FTL {
     macro(C_JITOperation_EJssJss, functionType(intPtr, intPtr, intPtr, intPtr)) \
     macro(C_JITOperation_EJssJssJss, functionType(intPtr, intPtr, intPtr, intPtr, intPtr)) \
     macro(C_JITOperation_ESt, functionType(intPtr, intPtr, intPtr)) \
-    macro(C_JITOperation_EStJscSymtab, functionType(intPtr, intPtr, intPtr, intPtr, intPtr)) \
+    macro(C_JITOperation_EStJscSymtabJ, functionType(intPtr, intPtr, intPtr, intPtr, intPtr, intPtr)) \
     macro(C_JITOperation_EStRZJsf, functionType(intPtr, intPtr, intPtr, intPtr, int32, intPtr)) \
     macro(C_JITOperation_EStRZJsfL, functionType(intPtr, intPtr, intPtr, intPtr, int32, intPtr, intPtr)) \
     macro(C_JITOperation_EStZ, functionType(intPtr, intPtr, intPtr, int32)) \
index c9dde55..0e1ec09 100644 (file)
@@ -3056,11 +3056,12 @@ private:
         LValue scope = lowCell(m_node->child1());
         SymbolTable* table = m_node->castOperand<SymbolTable*>();
         Structure* structure = m_graph.globalObjectFor(m_node->origin.semantic)->activationStructure();
-        
+        JSValue initializationValue = m_node->initializationValueForActivation();
+        ASSERT(initializationValue.isUndefined() || initializationValue == jsTDZValue());
         if (table->singletonScope()->isStillValid()) {
             LValue callResult = vmCall(
                 m_out.operation(operationCreateActivationDirect), m_callFrame, weakPointer(structure),
-                scope, weakPointer(table));
+                scope, weakPointer(table), m_out.constInt64(JSValue::encode(initializationValue)));
             setJSValue(callResult);
             return;
         }
@@ -3080,7 +3081,7 @@ private:
         
         for (unsigned i = 0; i < table->scopeSize(); ++i) {
             m_out.store64(
-                m_out.constInt64(JSValue::encode(jsUndefined())),
+                m_out.constInt64(JSValue::encode(initializationValue)),
                 fastObject, m_heaps.JSEnvironmentRecord_variables[i]);
         }
         
@@ -3090,7 +3091,7 @@ private:
         m_out.appendTo(slowPath, continuation);
         LValue callResult = vmCall(
             m_out.operation(operationCreateActivationDirect), m_callFrame, weakPointer(structure),
-            scope, weakPointer(table));
+            scope, weakPointer(table), m_out.constInt64(JSValue::encode(initializationValue)));
         ValueFromBlock slowResult = m_out.anchor(callResult);
         m_out.jump(continuation);
         
@@ -5409,9 +5410,14 @@ private:
         m_out.jump(continuation);
 
         m_out.appendTo(slowPath, continuation);
+        // We ensure allocation sinking explictly sets bottom values for all field members. 
+        // Therefore, it doesn't matter what JSValue we pass in as the initialization value
+        // because all fields will be overwritten.
+        // FIXME: It may be worth creating an operation that calls a constructor on JSLexicalEnvironment that 
+        // doesn't initialize every slot because we are guaranteed to do that here.
         LValue callResult = vmCall(
             m_out.operation(operationCreateActivationDirect), m_callFrame, weakPointer(structure),
-            scope, weakPointer(table));
+            scope, weakPointer(table), m_out.constInt64(JSValue::encode(jsUndefined())));
         ValueFromBlock slowResult =  m_out.anchor(callResult);
         m_out.jump(continuation);
 
index 4c38373..8cdbc5d 100644 (file)
@@ -191,7 +191,10 @@ extern "C" JSCell* JIT_OPERATION operationMaterializeObjectInOSR(
             materialization->origin(), exec->codeBlock());
         Structure* structure = codeBlock->globalObject()->activationStructure();
 
-        JSLexicalEnvironment* result = JSLexicalEnvironment::create(vm, structure, scope, table);
+        // It doesn't matter what values we initialize as bottom values inside the activation constructor because
+        // activation sinking will set bottom values for each slot.
+        // FIXME: Slight optimization would be to create a constructor that doesn't initialize all slots.
+        JSLexicalEnvironment* result = JSLexicalEnvironment::create(vm, structure, scope, table, jsUndefined());
 
         RELEASE_ASSERT(materialization->properties().size() - 2 == table->scopeSize());
 
index c997d2c..687140e 100644 (file)
@@ -1135,12 +1135,18 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue
     } else {
         for (JSScope* node = scope; ; node = node->next()) {
             RELEASE_ASSERT(node);
-            if (node->isVariableObject()) {
-                ASSERT(!node->isNameScopeObject());
+            if (node->isGlobalObject()) {
                 variableObject = node;
                 break;
+            } 
+            if (JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<JSLexicalEnvironment*>(node)) {
+                if (!lexicalEnvironment->symbolTable()->correspondsToLexicalScope()) {
+                    variableObject = node;
+                    break;
+                }
             }
         }
+        ASSERT(!variableObject->isNameScopeObject());
     }
 
     JSObject* compileError = eval->prepareForExecution(callFrame, nullptr, scope, CodeForCall);
index b69293b..a66aaf6 100644 (file)
@@ -349,6 +349,17 @@ public:
         addCallArgument(arg5);
     }
 
+    ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImmPtr arg1, GPRReg arg2, TrustedImmPtr arg3, TrustedImm32 arg4, TrustedImm32 arg5)
+    { 
+        resetCallArguments();
+        addCallArgument(GPRInfo::callFrameRegister);
+        addCallArgument(arg1);
+        addCallArgument(arg2);
+        addCallArgument(arg3);
+        addCallArgument(arg4);
+        addCallArgument(arg5);
+    }
+
     ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, GPRReg arg2, GPRReg arg3, TrustedImm32 arg4, GPRReg arg5, TrustedImm32 arg6)
     {
         resetCallArguments();
@@ -1516,6 +1527,13 @@ public:
         setupArgumentsWithExecState(arg1, arg2, arg3);
     }
 
+    ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImmPtr arg1, GPRReg arg2, TrustedImmPtr arg3, TrustedImm32 arg4, TrustedImm32 arg5)
+    {
+        poke(arg5, POKE_ARGUMENT_OFFSET + 1);
+        poke(arg4, POKE_ARGUMENT_OFFSET);
+        setupArgumentsWithExecState(arg1, arg2, arg3);
+    }
+
     ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImmPtr arg1, GPRReg arg2, TrustedImm32 arg3, TrustedImmPtr arg4)
     {
         poke(arg4, POKE_ARGUMENT_OFFSET);
@@ -1916,6 +1934,25 @@ public:
         move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
     }
 
+    ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImmPtr arg1, GPRReg arg2, TrustedImmPtr arg3, TrustedImm64 arg4)
+    {
+        move(arg2, GPRInfo::argumentGPR2); // In case arg2 is argumentGPR1.
+        move(arg1, GPRInfo::argumentGPR1);
+        move(arg3, GPRInfo::argumentGPR3);
+        move(arg4, GPRInfo::argumentGPR4);
+        move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+    }
+
+    ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImmPtr arg1, GPRReg arg2, TrustedImmPtr arg3, TrustedImm32 arg4, TrustedImm32 arg5)
+    {
+        move(arg2, GPRInfo::argumentGPR2); // In case arg2 is argumentGPR1.
+        move(arg1, GPRInfo::argumentGPR1);
+        move(arg3, GPRInfo::argumentGPR3);
+        move(arg4, GPRInfo::argumentGPR4);
+        move(arg5, GPRInfo::argumentGPR5);
+        move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+    }
+
     ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, TrustedImmPtr arg2, GPRReg arg3, TrustedImm32 arg4, TrustedImm32 arg5)
     {
         move(arg3, GPRInfo::argumentGPR3);
index 23d31da..d7693b1 100644 (file)
@@ -218,7 +218,6 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_div)
         DEFINE_OP(op_end)
         DEFINE_OP(op_enter)
-        DEFINE_OP(op_create_lexical_environment)
         DEFINE_OP(op_get_scope)
         DEFINE_OP(op_eq)
         DEFINE_OP(op_eq_null)
@@ -264,7 +263,6 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_new_regexp)
         DEFINE_OP(op_not)
         DEFINE_OP(op_nstricteq)
-        DEFINE_OP(op_pop_scope)
         DEFINE_OP(op_dec)
         DEFINE_OP(op_inc)
         DEFINE_OP(op_profile_did_call)
@@ -273,6 +271,8 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_profile_control_flow)
         DEFINE_OP(op_push_name_scope)
         DEFINE_OP(op_push_with_scope)
+        DEFINE_OP(op_create_lexical_environment)
+        DEFINE_OP(op_get_parent_scope)
         case op_put_by_id_out_of_line:
         case op_put_by_id_transition_direct:
         case op_put_by_id_transition_normal:
index 202c99c..fdafd6e 100644 (file)
@@ -478,7 +478,6 @@ namespace JSC {
         void emit_op_div(Instruction*);
         void emit_op_end(Instruction*);
         void emit_op_enter(Instruction*);
-        void emit_op_create_lexical_environment(Instruction*);
         void emit_op_get_scope(Instruction*);
         void emit_op_eq(Instruction*);
         void emit_op_eq_null(Instruction*);
@@ -525,7 +524,6 @@ namespace JSC {
         void emit_op_new_regexp(Instruction*);
         void emit_op_not(Instruction*);
         void emit_op_nstricteq(Instruction*);
-        void emit_op_pop_scope(Instruction*);
         void emit_op_dec(Instruction*);
         void emit_op_inc(Instruction*);
         void emit_op_profile_did_call(Instruction*);
@@ -534,6 +532,8 @@ namespace JSC {
         void emit_op_profile_control_flow(Instruction*);
         void emit_op_push_name_scope(Instruction*);
         void emit_op_push_with_scope(Instruction*);
+        void emit_op_create_lexical_environment(Instruction*);
+        void emit_op_get_parent_scope(Instruction*);
         void emit_op_put_by_id(Instruction*);
         void emit_op_put_by_index(Instruction*);
         void emit_op_put_by_val(Instruction*);
@@ -714,6 +714,7 @@ namespace JSC {
 #endif
         MacroAssembler::Call callOperation(J_JITOperation_EP, int, void*);
         MacroAssembler::Call callOperation(WithProfileTag, J_JITOperation_EPc, int, Instruction*);
+        MacroAssembler::Call callOperation(J_JITOperation_EPc, int, Instruction*);
         MacroAssembler::Call callOperation(J_JITOperation_EZ, int, int32_t);
         MacroAssembler::Call callOperation(J_JITOperation_EZZ, int, int32_t, int32_t);
         MacroAssembler::Call callOperation(P_JITOperation_EJS, GPRReg, size_t);
@@ -729,6 +730,7 @@ namespace JSC {
         MacroAssembler::Call callOperation(V_JITOperation_ECICC, RegisterID, const Identifier*, RegisterID, RegisterID);
         MacroAssembler::Call callOperation(J_JITOperation_EE, RegisterID);
         MacroAssembler::Call callOperation(V_JITOperation_EZSymtabJ, int, SymbolTable*, RegisterID);
+        MacroAssembler::Call callOperation(J_JITOperation_EZSymtabJ, int, SymbolTable*, RegisterID);
         MacroAssembler::Call callOperation(V_JITOperation_EJ, RegisterID);
 #if USE(JSVALUE64)
         MacroAssembler::Call callOperationNoExceptionCheck(V_JITOperation_EJ, RegisterID);
index a21c808..a49484a 100644 (file)
@@ -283,6 +283,12 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(WithProfileTag, J_JITOpera
     return appendCallWithExceptionCheckSetJSValueResultWithProfile(operation, dst);
 }
 
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(J_JITOperation_EPc operation, int dst, Instruction* bytecodePC)
+{
+    setupArgumentsWithExecState(TrustedImmPtr(bytecodePC));
+    return appendCallWithExceptionCheckSetJSValueResult(operation, dst);
+}
+
 ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(J_JITOperation_EZ operation, int dst, int32_t arg)
 {
     setupArgumentsWithExecState(TrustedImm32(arg));
@@ -484,6 +490,12 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EZSymtabJ o
     return appendCallWithExceptionCheck(operation);
 }
 
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(J_JITOperation_EZSymtabJ operation, int op1, SymbolTable* symbolTable, RegisterID regOp3)
+{
+    setupArgumentsWithExecState(TrustedImm32(op1), TrustedImmPtr(symbolTable), regOp3);
+    return appendCallWithExceptionCheck(operation);
+}
+
 ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EJ operation, RegisterID regOp)
 {
     setupArgumentsWithExecState(regOp);
index 0813b15..c85871b 100644 (file)
@@ -431,13 +431,6 @@ void JIT::emit_op_push_with_scope(Instruction* currentInstruction)
     callOperation(operationPushWithScope, dst, regT0);
 }
 
-void JIT::emit_op_pop_scope(Instruction* currentInstruction)
-{
-    int scope = currentInstruction[1].u.operand;
-
-    callOperation(operationPopScope, scope);
-}
-
 void JIT::compileOpStrictEq(Instruction* currentInstruction, CompileOpStrictEqType type)
 {
     int dst = currentInstruction[1].u.operand;
@@ -535,6 +528,20 @@ void JIT::emit_op_catch(Instruction* currentInstruction)
     emitPutVirtualRegister(currentInstruction[2].u.operand);
 }
 
+void JIT::emit_op_create_lexical_environment(Instruction* currentInstruction)
+{
+    JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_create_lexical_environment);
+    slowPathCall.call();
+}
+
+void JIT::emit_op_get_parent_scope(Instruction* currentInstruction)
+{
+    int currentScope = currentInstruction[2].u.operand;
+    emitGetVirtualRegister(currentScope, regT0);
+    loadPtr(Address(regT0, JSScope::offsetOfNext()), regT0);
+    emitStoreCell(currentInstruction[1].u.operand, regT0);
+}
+
 void JIT::emit_op_switch_imm(Instruction* currentInstruction)
 {
     size_t tableIndex = currentInstruction[1].u.operand;
@@ -673,17 +680,6 @@ void JIT::emit_op_enter(Instruction*)
     emitEnterOptimizationCheck();
 }
 
-void JIT::emit_op_create_lexical_environment(Instruction* currentInstruction)
-{
-    int dst = currentInstruction[1].u.operand;
-    int scope = currentInstruction[2].u.operand;
-
-    emitGetVirtualRegister(scope, regT0);
-    callOperation(operationCreateActivation, regT0);
-    emitStoreCell(dst, returnValueGPR);
-    emitStoreCell(scope, returnValueGPR);
-}
-
 void JIT::emit_op_get_scope(Instruction* currentInstruction)
 {
     int dst = currentInstruction[1].u.operand;
index 8764cc2..932657f 100644 (file)
@@ -754,12 +754,6 @@ void JIT::emit_op_push_with_scope(Instruction* currentInstruction)
     callOperation(operationPushWithScope, dst, regT1, regT0);
 }
 
-void JIT::emit_op_pop_scope(Instruction* currentInstruction)
-{
-    int scope = currentInstruction[1].u.operand;
-    callOperation(operationPopScope, scope);
-}
-
 void JIT::emit_op_to_number(Instruction* currentInstruction)
 {
     int dst = currentInstruction[1].u.operand;
@@ -845,6 +839,20 @@ void JIT::emit_op_catch(Instruction* currentInstruction)
     emitStore(thrownValue, regT1, regT0);
 }
 
+void JIT::emit_op_create_lexical_environment(Instruction* currentInstruction)
+{
+    JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_create_lexical_environment);
+    slowPathCall.call();
+}
+
+void JIT::emit_op_get_parent_scope(Instruction* currentInstruction)
+{
+    int currentScope = currentInstruction[2].u.operand;
+    emitLoadPayload(currentScope, regT0);
+    loadPtr(Address(regT0, JSScope::offsetOfNext()), regT0);
+    emitStoreCell(currentInstruction[1].u.operand, regT0);
+}
+
 void JIT::emit_op_switch_imm(Instruction* currentInstruction)
 {
     size_t tableIndex = currentInstruction[1].u.operand;
@@ -921,17 +929,6 @@ void JIT::emit_op_enter(Instruction* currentInstruction)
     slowPathCall.call();
 }
 
-void JIT::emit_op_create_lexical_environment(Instruction* currentInstruction)
-{
-    int lexicalEnvironment = currentInstruction[1].u.operand;
-    int scope = currentInstruction[2].u.operand;
-
-    emitLoadPayload(currentInstruction[2].u.operand, regT0);
-    callOperation(operationCreateActivation, regT0);
-    emitStoreCell(lexicalEnvironment, returnValueGPR);
-    emitStoreCell(scope, returnValueGPR);
-}
-
 void JIT::emit_op_get_scope(Instruction* currentInstruction)
 {
     int dst = currentInstruction[1].u.operand;
index 38015d2..e84e934 100644 (file)
@@ -1491,14 +1491,6 @@ EncodedJSValue JIT_OPERATION operationCheckHasInstance(ExecState* exec, EncodedJ
     return JSValue::encode(JSValue());
 }
 
-JSCell* JIT_OPERATION operationCreateActivation(ExecState* exec, JSScope* currentScope)
-{
-    VM& vm = exec->vm();
-    NativeCallFrameTracer tracer(&vm, exec);
-    JSLexicalEnvironment* lexicalEnvironment = JSLexicalEnvironment::create(vm, exec, currentScope, exec->codeBlock());
-    return lexicalEnvironment;
-}
-
 }
 
 static bool canAccessArgumentIndexQuickly(JSObject& object, uint32_t index)
index 7de77fc..64df4f3 100644 (file)
@@ -126,6 +126,7 @@ typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_ESsiJI)(ExecState*, Struct
 typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EZ)(ExecState*, int32_t);
 typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EZIcfZ)(ExecState*, int32_t, InlineCallFrame*, int32_t);
 typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EZZ)(ExecState*, int32_t, int32_t);
+typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EZSymtabJ)(ExecState*, int32_t, SymbolTable*, EncodedJSValue);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_E)(ExecState*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EZ)(ExecState*, int32_t);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EC)(ExecState*, JSCell*);
@@ -148,7 +149,7 @@ typedef JSCell* JIT_OPERATION (*C_JITOperation_EL)(ExecState*, JSLexicalEnvironm
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EO)(ExecState*, JSObject*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EOZ)(ExecState*, JSObject*, int32_t);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_ESt)(ExecState*, Structure*);
-typedef JSCell* JIT_OPERATION (*C_JITOperation_EStJscSymtab)(ExecState*, Structure*, JSScope*, SymbolTable*);
+typedef JSCell* JIT_OPERATION (*C_JITOperation_EStJscSymtabJ)(ExecState*, Structure*, JSScope*, SymbolTable*, EncodedJSValue);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EStRZJsfL)(ExecState*, Structure*, Register*, int32_t, JSFunction*, JSLexicalEnvironment*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EStRZJsf)(ExecState*, Structure*, Register*, int32_t, JSFunction*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EStZ)(ExecState*, Structure*, int32_t);
@@ -317,7 +318,6 @@ void JIT_OPERATION operationPopScope(ExecState*, int32_t) WTF_INTERNAL;
 void JIT_OPERATION operationProfileDidCall(ExecState*, EncodedJSValue) WTF_INTERNAL;
 void JIT_OPERATION operationProfileWillCall(ExecState*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationCheckHasInstance(ExecState*, EncodedJSValue, EncodedJSValue baseVal) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationCreateActivation(ExecState*, JSScope* currentScope) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValDefault(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ArrayProfile*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValGeneric(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ArrayProfile*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValString(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript) WTF_INTERNAL;
index 9cfe873..1934e57 100644 (file)
@@ -32,6 +32,7 @@
 #include "CommonSlowPathsExceptions.h"
 #include "Error.h"
 #include "ErrorHandlingScope.h"
+#include "Exception.h"
 #include "ExceptionFuzz.h"
 #include "GetterSetter.h"
 #include "HostCallReturnValue.h"
@@ -488,18 +489,6 @@ LLINT_SLOW_PATH_DECL(stack_check)
     LLINT_RETURN_TWO(pc, exec);
 }
 
-LLINT_SLOW_PATH_DECL(slow_path_create_lexical_environment)
-{
-    LLINT_BEGIN();
-#if LLINT_SLOW_PATH_TRACING
-    dataLogF("Creating an lexicalEnvironment, exec = %p!\n", exec);
-#endif
-    int scopeReg = pc[2].u.operand;
-    JSScope* scope = exec->uncheckedR(scopeReg).Register::scope();
-    JSLexicalEnvironment* lexicalEnvironment = JSLexicalEnvironment::create(vm, exec, scope, exec->codeBlock());
-    LLINT_RETURN(JSValue(lexicalEnvironment));
-}
-
 LLINT_SLOW_PATH_DECL(slow_path_new_object)
 {
     LLINT_BEGIN();
@@ -1289,15 +1278,6 @@ LLINT_SLOW_PATH_DECL(slow_path_push_with_scope)
     LLINT_END();
 }
 
-LLINT_SLOW_PATH_DECL(slow_path_pop_scope)
-{
-    LLINT_BEGIN();
-    int scopeReg = pc[1].u.operand;
-    JSScope* scope = exec->uncheckedR(scopeReg).Register::scope();
-    exec->uncheckedR(scopeReg) = scope->next();
-    LLINT_END();
-}
-
 LLINT_SLOW_PATH_DECL(slow_path_push_name_scope)
 {
     LLINT_BEGIN();
index f8f3864..7b2b753 100644 (file)
@@ -62,7 +62,6 @@ LLINT_SLOW_PATH_HIDDEN_DECL(entry_osr_function_for_construct_arityCheck);
 LLINT_SLOW_PATH_HIDDEN_DECL(loop_osr);
 LLINT_SLOW_PATH_HIDDEN_DECL(replace);
 LLINT_SLOW_PATH_HIDDEN_DECL(stack_check);
-LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_create_lexical_environment);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_object);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_array);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_array_with_size);
@@ -108,7 +107,6 @@ LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_tear_off_arguments);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_strcat);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_to_primitive);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_push_with_scope);
-LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_pop_scope);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_push_name_scope);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_throw);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_throw_static_error);
index d9cd01b..7391b13 100644 (file)
@@ -1287,15 +1287,15 @@ _llint_op_push_with_scope:
     dispatch(3)
 
 
-_llint_op_pop_scope:
+_llint_op_push_name_scope:
     traceExecution()
-    callSlowPath(_llint_slow_path_pop_scope)
-    dispatch(2)
+    callSlowPath(_llint_slow_path_push_name_scope)
+    dispatch(5)
 
 
-_llint_op_push_name_scope:
+_llint_op_create_lexical_environment:
     traceExecution()
-    callSlowPath(_llint_slow_path_push_name_scope)
+    callSlowPath(_slow_path_create_lexical_environment)
     dispatch(5)
 
 
index 119b89a..4738d28 100644 (file)
@@ -720,12 +720,6 @@ _llint_op_enter:
     dispatch(1)
 
 
-_llint_op_create_lexical_environment:
-    traceExecution()
-    callSlowPath(_llint_slow_path_create_lexical_environment)
-    dispatch(3)
-
-
 _llint_op_get_scope:
     traceExecution()
     loadi Callee + PayloadOffset[cfr], t0
@@ -793,8 +787,9 @@ _llint_op_new_object:
 
 _llint_op_check_tdz:
     traceExecution()
-    loadpFromInstruction(1, t0)
-    bineq TagOffset[cfr, t0, 8], EmptyValueTag, .opNotTDZ
+    loadisFromInstruction(1, t0)
+    loadConstantOrVariableTag(t0, t1)
+    bineq t1, EmptyValueTag, .opNotTDZ
     callSlowPath(_slow_path_throw_tdz_error)
 
 .opNotTDZ:
@@ -2336,6 +2331,17 @@ _llint_op_put_to_arguments:
     dispatch(4)
 
 
+_llint_op_get_parent_scope:
+    traceExecution()
+    loadisFromInstruction(2, t0)
+    loadp PayloadOffset[cfr, t0, 8], t0
+    loadp JSScope::m_next[t0], t0
+    loadisFromInstruction(1, t1)
+    storei CellTag, TagOffset[cfr, t1, 8]
+    storei t0, PayloadOffset[cfr, t1, 8]
+    dispatch(3)
+
+
 _llint_op_profile_type:
     traceExecution()
     loadp CodeBlock[cfr], t1
index 43e997b..f8e4ed1 100644 (file)
@@ -607,12 +607,6 @@ _llint_op_enter:
     dispatch(1)
 
 
-_llint_op_create_lexical_environment:
-    traceExecution()
-    callSlowPath(_llint_slow_path_create_lexical_environment)
-    dispatch(3)
-
-
 _llint_op_get_scope:
     traceExecution()
     loadp Callee[cfr], t0
@@ -678,9 +672,9 @@ _llint_op_new_object:
 
 _llint_op_check_tdz:
     traceExecution()
-    loadpFromInstruction(1, t0)
-    loadq [cfr, t0, 8], t0
-    bqneq t0, ValueEmpty, .opNotTDZ
+    loadisFromInstruction(1, t0)
+    loadConstantOrVariable(t0, t1)
+    bqneq t1, ValueEmpty, .opNotTDZ
     callSlowPath(_slow_path_throw_tdz_error)
 
 .opNotTDZ:
@@ -2194,6 +2188,15 @@ _llint_op_put_to_arguments:
     dispatch(4)
 
 
+_llint_op_get_parent_scope:
+    traceExecution()
+    loadVariable(2, t0)
+    loadp JSScope::m_next[t0], t0
+    loadisFromInstruction(1, t1)
+    storeq t0, [cfr, t1, 8]
+    dispatch(3)
+
+
 _llint_op_profile_type:
     traceExecution()
     loadp CodeBlock[cfr], t1
index 0fb0f29..766aac1 100644 (file)
@@ -30,6 +30,7 @@
 #include "BytecodeIntrinsicRegistry.h"
 #include "NodeConstructors.h"
 #include "SyntaxChecker.h"
+#include "VariableEnvironment.h"
 #include <utility>
 
 namespace JSC {
@@ -134,7 +135,6 @@ public:
 
     JSC::SourceElements* createSourceElements() { return new (m_parserArena) JSC::SourceElements(); }
 
-    DeclarationStacks::VarStack& varDeclarations() { return m_scope.m_varDeclarations; }
     DeclarationStacks::FunctionStack& funcDeclarations() { return m_scope.m_funcDeclarations; }
     int features() const { return m_scope.m_features; }
     int numConstants() const { return m_scope.m_numConstants; }
@@ -327,11 +327,11 @@ public:
         return new (m_parserArena) ConditionalNode(location, condition, lhs, rhs);
     }
 
-    ExpressionNode* createAssignResolve(const JSTokenLocation& location, const Identifier& ident, ExpressionNode* rhs, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end)
+    ExpressionNode* createAssignResolve(const JSTokenLocation& location, const Identifier& ident, ExpressionNode* rhs, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end, AssignmentContext assignmentContext)
     {
         if (rhs->isFuncExprNode())
             static_cast<FuncExprNode*>(rhs)->body()->setInferredName(ident);
-        AssignResolveNode* node = new (m_parserArena) AssignResolveNode(location, ident, rhs);
+        AssignResolveNode* node = new (m_parserArena) AssignResolveNode(location, ident, rhs, assignmentContext);
         setExceptionLocation(node, start, divot, end);
         return node;
     }
@@ -443,16 +443,16 @@ public:
         const JSTextPosition& classStart, const JSTextPosition& classEnd, unsigned startLine, unsigned endLine)
     {
         // FIXME: Use "let" declaration.
-        ExpressionNode* assign = createAssignResolve(location, classExpression->name(), classExpression, classStart, classStart + 1, classEnd);
+        ExpressionNode* assign = createAssignResolve(location, classExpression->name(), classExpression, classStart, classStart + 1, classEnd, AssignmentContext::DeclarationStatement);
         ClassDeclNode* decl = new (m_parserArena) ClassDeclNode(location, assign);
         decl->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset);
         return decl;
     }
 #endif
 
-    StatementNode* createBlockStatement(const JSTokenLocation& location, JSC::SourceElements* elements, int startLine, int endLine)
+    StatementNode* createBlockStatement(const JSTokenLocation& location, JSC::SourceElements* elements, int startLine, int endLine, VariableEnvironment& lexicalVariables)
     {
-        BlockNode* block = new (m_parserArena) BlockNode(location, elements);
+        BlockNode* block = new (m_parserArena) BlockNode(location, elements, lexicalVariables);
         block->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset);
         return block;
     }
@@ -471,39 +471,39 @@ public:
         return result;
     }
 
-    StatementNode* createForLoop(const JSTokenLocation& location, ExpressionNode* initializer, ExpressionNode* condition, ExpressionNode* iter, StatementNode* statements, int start, int end)
+    StatementNode* createForLoop(const JSTokenLocation& location, ExpressionNode* initializer, ExpressionNode* condition, ExpressionNode* iter, StatementNode* statements, int start, int end, VariableEnvironment& lexicalVariables)
     {
-        ForNode* result = new (m_parserArena) ForNode(location, initializer, condition, iter, statements);
+        ForNode* result = new (m_parserArena) ForNode(location, initializer, condition, iter, statements, lexicalVariables);
         result->setLoc(start, end, location.startOffset, location.lineStartOffset);
         return result;
     }
 
-    StatementNode* createForInLoop(const JSTokenLocation& location, ExpressionNode* lhs, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end)
+    StatementNode* createForInLoop(const JSTokenLocation& location, ExpressionNode* lhs, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end, VariableEnvironment& lexicalVariables)
     {
-        ForInNode* result = new (m_parserArena) ForInNode(location, lhs, iter, statements);
+        ForInNode* result = new (m_parserArena) ForInNode(location, lhs, iter, statements, lexicalVariables);
         result->setLoc(start, end, location.startOffset, location.lineStartOffset);
         setExceptionLocation(result, eStart, eDivot, eEnd);
         return result;
     }
     
-    StatementNode* createForInLoop(const JSTokenLocation& location, PassRefPtr<DestructuringPatternNode> pattern, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end)
+    StatementNode* createForInLoop(const JSTokenLocation& location, PassRefPtr<DestructuringPatternNode> pattern, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end, VariableEnvironment& lexicalVariables)
     {
         auto lexpr = new (m_parserArena) DestructuringAssignmentNode(location, pattern.get(), 0);
-        return createForInLoop(location, lexpr, iter, statements, eStart, eDivot, eEnd, start, end);
+        return createForInLoop(location, lexpr, iter, statements, eStart, eDivot, eEnd, start, end, lexicalVariables);
     }
     
-    StatementNode* createForOfLoop(const JSTokenLocation& location, ExpressionNode* lhs, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end)
+    StatementNode* createForOfLoop(const JSTokenLocation& location, ExpressionNode* lhs, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end, VariableEnvironment& lexicalVariables)
     {
-        ForOfNode* result = new (m_parserArena) ForOfNode(location, lhs, iter, statements);
+        ForOfNode* result = new (m_parserArena) ForOfNode(location, lhs, iter, statements, lexicalVariables);
         result->setLoc(start, end, location.startOffset, location.lineStartOffset);
         setExceptionLocation(result, eStart, eDivot, eEnd);
         return result;
     }
     
-    StatementNode* createForOfLoop(const JSTokenLocation& location, PassRefPtr<DestructuringPatternNode> pattern, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end)
+    StatementNode* createForOfLoop(const JSTokenLocation& location, PassRefPtr<DestructuringPatternNode> pattern, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end, VariableEnvironment& lexicalVariables)
     {
         auto lexpr = new (m_parserArena) DestructuringAssignmentNode(location, pattern.get(), 0);
-        return createForOfLoop(location, lexpr, iter, statements, eStart, eDivot, eEnd, start, end);
+        return createForOfLoop(location, lexpr, iter, statements, eStart, eDivot, eEnd, start, end, lexicalVariables);
     }
 
     bool isBindingNode(const DestructuringPattern& pattern)
@@ -513,19 +513,34 @@ public:
 
     StatementNode* createEmptyStatement(const JSTokenLocation& location) { return new (m_parserArena) EmptyStatementNode(location); }
 
-    StatementNode* createVarStatement(const JSTokenLocation& location, ExpressionNode* expr, int start, int end)
+    StatementNode* createDeclarationStatement(const JSTokenLocation& location, ExpressionNode* expr, int start, int end)
     {
         StatementNode* result;
-        result = new (m_parserArena) VarStatementNode(location, expr);
+        result = new (m_parserArena) DeclarationStatement(location, expr);
         result->setLoc(start, end, location.startOffset, location.lineStartOffset);
         return result;
     }
 
+    StatementNode* createVarStatement(const JSTokenLocation& location, ExpressionNode* expr, int start, int end)
+    {
+        return createDeclarationStatement(location, expr, start, end);
+    }
+
+    StatementNode* createLetStatement(const JSTokenLocation& location, ExpressionNode* expr, int start, int end)
+    {
+        return createDeclarationStatement(location, expr, start, end);
+    }
+
     ExpressionNode* createEmptyVarExpression(const JSTokenLocation& location, const Identifier& identifier)
     {
         return new (m_parserArena) EmptyVarExpression(location, identifier);
     }
 
+    ExpressionNode* createEmptyLetExpression(const JSTokenLocation& location, const Identifier& identifier)
+    {
+        return new (m_parserArena) EmptyLetExpression(location, identifier);
+    }
+
     StatementNode* createReturnStatement(const JSTokenLocation& location, ExpressionNode* expression, const JSTextPosition& start, const JSTextPosition& end)
     {
         ReturnNode* result = new (m_parserArena) ReturnNode(location, expression);
@@ -559,10 +574,10 @@ public:
         return result;
     }
 
-    StatementNode* createSwitchStatement(const JSTokenLocation& location, ExpressionNode* expr, ClauseListNode* firstClauses, CaseClauseNode* defaultClause, ClauseListNode* secondClauses, int startLine, int endLine)
+    StatementNode* createSwitchStatement(const JSTokenLocation& location, ExpressionNode* expr, ClauseListNode* firstClauses, CaseClauseNode* defaultClause, ClauseListNode* secondClauses, int startLine, int endLine, VariableEnvironment& lexicalVariables)
     {
         CaseBlockNode* cases = new (m_parserArena) CaseBlockNode(firstClauses, defaultClause, secondClauses);
-        SwitchNode* result = new (m_parserArena) SwitchNode(location, expr, cases);
+        SwitchNode* result = new (m_parserArena) SwitchNode(location, expr, cases, lexicalVariables);
         result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset);
         return result;
     }
@@ -631,14 +646,6 @@ public:
         elements->append(statement);
     }
 
-    void addVar(const Identifier* ident, int attrs)
-    {
-        if (m_vm->propertyNames->arguments == *ident)
-            usesArguments();
-        ASSERT(ident->impl()->isAtomic() || ident->impl()->isSymbol());
-        m_scope.m_varDeclarations.append(std::make_pair(*ident, attrs));
-    }
-
     CommaNode* createCommaExpr(const JSTokenLocation& location, ExpressionNode* node)
     {
         return new (m_parserArena) CommaNode(location, node);
@@ -778,9 +785,9 @@ public:
         node->appendEntry(location, identifier, wasString, pattern.get(), defaultValue);
     }
     
-    BindingPattern createBindingLocation(const JSTokenLocation&, const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end)
+    BindingPattern createBindingLocation(const JSTokenLocation&, const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end, AssignmentContext context)
     {
-        return BindingNode::create(boundProperty, start, end);
+        return BindingNode::create(boundProperty, start, end, context);
     }
 
     void setEndOffset(Node* node, int offset)
@@ -810,7 +817,6 @@ private:
             , m_numConstants(0)
         {
         }
-        DeclarationStacks::VarStack m_varDeclarations;
         DeclarationStacks::FunctionStack m_funcDeclarations;
         int m_features;
         int m_numConstants;
@@ -1167,7 +1173,7 @@ ExpressionNode* ASTBuilder::makeAssignNode(const JSTokenLocation& location, Expr
         if (op == OpEqual) {
             if (expr->isFuncExprNode())
                 static_cast<FuncExprNode*>(expr)->body()->setInferredName(resolve->identifier());
-            AssignResolveNode* node = new (m_parserArena) AssignResolveNode(location, resolve->identifier(), expr);
+            AssignResolveNode* node = new (m_parserArena) AssignResolveNode(location, resolve->identifier(), expr, AssignmentContext::AssignmentExpression);
             setExceptionLocation(node, start, divot, end);
             return node;
         }
index 3c3f95f..29cfd14 100644 (file)
@@ -19,6 +19,7 @@ for           FOR
 instanceof     INSTANCEOF
 new            NEW
 var            VAR
+let            LET
 continue       CONTINUE
 function       FUNCTION
 return         RETURN
@@ -46,7 +47,6 @@ import          RESERVED
 # Reserved for future use in strict code.
 implements      RESERVED_IF_STRICT
 interface       RESERVED_IF_STRICT
-let             RESERVED_IF_STRICT
 package         RESERVED_IF_STRICT
 private         RESERVED_IF_STRICT
 protected       RESERVED_IF_STRICT
index 60c7c2f..e1e8487 100644 (file)
@@ -634,10 +634,11 @@ namespace JSC {
     {
     }
 
-    inline AssignResolveNode::AssignResolveNode(const JSTokenLocation& location, const Identifier& ident, ExpressionNode* right)
+    inline AssignResolveNode::AssignResolveNode(const JSTokenLocation& location, const Identifier& ident, ExpressionNode* right, AssignmentContext assignmentContext)
         : ExpressionNode(location)
         , m_ident(ident)
         , m_right(right)
+        , m_assignmentContext(assignmentContext)
     {
     }
 
@@ -727,7 +728,7 @@ namespace JSC {
     {
     }
 
-    inline VarStatementNode::VarStatementNode(const JSTokenLocation& location, ExpressionNode* expr)
+    inline DeclarationStatement::DeclarationStatement(const JSTokenLocation& location, ExpressionNode* expr)
         : StatementNode(location)
         , m_expr(expr)
     {
@@ -738,6 +739,12 @@ namespace JSC {
         , m_ident(ident)
     {
     }
+
+    inline EmptyLetExpression::EmptyLetExpression(const JSTokenLocation& location, const Identifier& ident)
+        : ExpressionNode(location)
+        , m_ident(ident)
+    {
+    }
     
     inline IfElseNode::IfElseNode(const JSTokenLocation& location, ExpressionNode* condition, StatementNode* ifBlock, StatementNode* elseBlock)
         : StatementNode(location)
@@ -761,8 +768,9 @@ namespace JSC {
     {
     }
 
-    inline ForNode::ForNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, ExpressionNode* expr3, StatementNode* statement)
+    inline ForNode::ForNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, ExpressionNode* expr3, StatementNode* statement, VariableEnvironment& lexicalVariables)
         : StatementNode(location)
+        , VariableEnvironmentNode(lexicalVariables)
         , m_expr1(expr1)
         , m_expr2(expr2)
         , m_expr3(expr3)
@@ -894,8 +902,9 @@ namespace JSC {
     {
     }
 
-    inline SwitchNode::SwitchNode(const JSTokenLocation& location, ExpressionNode* expr, CaseBlockNode* block)
+    inline SwitchNode::SwitchNode(const JSTokenLocation& location, ExpressionNode* expr, CaseBlockNode* block, VariableEnvironment& lexicalVariables)
         : StatementNode(location)
+        , VariableEnvironmentNode(lexicalVariables)
         , m_expr(expr)
         , m_block(block)
     {
@@ -909,28 +918,30 @@ namespace JSC {
     {
     }
 
-    inline BlockNode::BlockNode(const JSTokenLocation& location, SourceElements* statements)
+    inline BlockNode::BlockNode(const JSTokenLocation& location, SourceElements* statements, VariableEnvironment& lexicalVariables)
         : StatementNode(location)
+        , VariableEnvironmentNode(lexicalVariables)
         , m_statements(statements)
     {
     }
 
-    inline EnumerationNode::EnumerationNode(const JSTokenLocation& location, ExpressionNode* l, ExpressionNode* expr, StatementNode* statement)
+    inline EnumerationNode::EnumerationNode(const JSTokenLocation& location, ExpressionNode* lexpr, ExpressionNode* expr, StatementNode* statement, VariableEnvironment& lexicalVariables)
         : StatementNode(location)
-        , m_lexpr(l)
+        , VariableEnvironmentNode(lexicalVariables)
+        , m_lexpr(lexpr)
         , m_expr(expr)
         , m_statement(statement)
     {
-        ASSERT(l);
+        ASSERT(lexpr);
     }
     
-    inline ForInNode::ForInNode(const JSTokenLocation& location, ExpressionNode* l, ExpressionNode* expr, StatementNode* statement)
-        : EnumerationNode(location, l, expr, statement)
+    inline ForInNode::ForInNode(const JSTokenLocation& location, ExpressionNode* lexpr, ExpressionNode* expr, StatementNode* statement, VariableEnvironment& lexicalVariables)
+        : EnumerationNode(location, lexpr, expr, statement, lexicalVariables)
     {
     }
     
-    inline ForOfNode::ForOfNode(const JSTokenLocation& location, ExpressionNode* l, ExpressionNode* expr, StatementNode* statement)
-        : EnumerationNode(location, l, expr, statement)
+    inline ForOfNode::ForOfNode(const JSTokenLocation& location, ExpressionNode* lexpr, ExpressionNode* expr, StatementNode* statement, VariableEnvironment& lexicalVariables)
+        : EnumerationNode(location, lexpr, expr, statement, lexicalVariables)
     {
     }
     
@@ -958,16 +969,17 @@ namespace JSC {
         return adoptRef(*new ObjectPatternNode);
     }
 
-    inline Ref<BindingNode> BindingNode::create(const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end)
+    inline Ref<BindingNode> BindingNode::create(const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end, AssignmentContext context)
     {
-        return adoptRef(*new BindingNode(boundProperty, start, end));
+        return adoptRef(*new BindingNode(boundProperty, start, end, context));
     }
     
-    inline BindingNode::BindingNode(const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end)
+    inline BindingNode::BindingNode(const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end, AssignmentContext context)
         : DestructuringPatternNode()
         , m_divotStart(start)
         , m_divotEnd(end)
         , m_boundProperty(boundProperty)
+        , m_bindingContext(context)
     {
     }
     
index 93a63a4..ff581f7 100644 (file)
@@ -94,9 +94,10 @@ ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocat
 {
 }
 
-ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, const SourceCode& source, SourceElements* children, VarStack& varStack, FunctionStack& funcStack, IdentifierSet& capturedVariables, CodeFeatures features, int numConstants)
+ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, const SourceCode& source, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack& funcStack, VariableEnvironment& lexicalVariables, CodeFeatures features, int numConstants)
     : StatementNode(endLocation)
     , ParserArenaRoot(parserArena)
+    , VariableEnvironmentNode(lexicalVariables)
     , m_startLineNumber(startLocation.line)
     , m_startStartOffset(startLocation.startOffset)
     , m_startLineStartOffset(startLocation.lineStartOffset)
@@ -105,9 +106,8 @@ ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocat
     , m_numConstants(numConstants)
     , m_statements(children)
 {
-    m_varStack.swap(varStack);
+    m_varDeclarations.swap(varEnvironment);
     m_functionStack.swap(funcStack);
-    m_capturedVariables.swap(capturedVariables);
 }
 
 StatementNode* ScopeNode::singleStatement() const
@@ -117,8 +117,8 @@ StatementNode* ScopeNode::singleStatement() const
 
 // ------------------------------ ProgramNode -----------------------------
 
-ProgramNode::ProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VarStack& varStack, FunctionStack& funcStack, IdentifierSet& capturedVariables, const SourceCode& source, CodeFeatures features, int numConstants)
-    : ScopeNode(parserArena, startLocation, endLocation, source, children, varStack, funcStack, capturedVariables, features, numConstants)
+ProgramNode::ProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack& funcStack, VariableEnvironment& lexicalVariables, const SourceCode& source, CodeFeatures features, int numConstants)
+    : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, funcStack, lexicalVariables, features, numConstants)
     , m_startColumn(startColumn)
     , m_endColumn(endColumn)
 {
@@ -131,8 +131,8 @@ void ProgramNode::setClosedVariables(Vector<RefPtr<UniquedStringImpl>>&& closedV
 
 // ------------------------------ EvalNode -----------------------------
 
-EvalNode::EvalNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned, unsigned endColumn, SourceElements* children, VarStack& varStack, FunctionStack& funcStack, IdentifierSet& capturedVariables, const SourceCode& source, CodeFeatures features, int numConstants)
-    : ScopeNode(parserArena, startLocation, endLocation, source, children, varStack, funcStack, capturedVariables, features, numConstants)
+EvalNode::EvalNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack& funcStack, VariableEnvironment& lexicalVariables, const SourceCode& source, CodeFeatures features, int numConstants)
+    : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, funcStack, lexicalVariables, features, numConstants)
     , m_endColumn(endColumn)
 {
 }
@@ -201,8 +201,8 @@ void FunctionBodyNode::setEndPosition(JSTextPosition position)
 
 // ------------------------------ FunctionNode -----------------------------
 
-FunctionNode::FunctionNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VarStack& varStack, FunctionStack& funcStack, IdentifierSet& capturedVariables, const SourceCode& sourceCode, CodeFeatures features, int numConstants)
-    : ScopeNode(parserArena, startLocation, endLocation, sourceCode, children, varStack, funcStack, capturedVariables, features, numConstants)
+FunctionNode::FunctionNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack& funcStack, VariableEnvironment& lexicalVariables, const SourceCode& sourceCode, CodeFeatures features, int numConstants)
+    : ScopeNode(parserArena, startLocation, endLocation, sourceCode, children, varEnvironment, funcStack, lexicalVariables, features, numConstants)
     , m_startColumn(startColumn)
     , m_endColumn(endColumn)
 {
@@ -216,4 +216,9 @@ void FunctionNode::finishParsing(PassRefPtr<FunctionParameters> parameters, cons
     m_functionMode = functionMode;
 }
 
+VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables)
+{
+    m_lexicalVariables.swap(lexicalVariables);
+}
+
 } // namespace JSC
index f29a510..08af647 100644 (file)
@@ -34,6 +34,7 @@
 #include "ResultType.h"
 #include "SourceCode.h"
 #include "SymbolTable.h"
+#include "VariableEnvironment.h"
 #include <wtf/MathExtras.h>
 
 namespace JSC {
@@ -79,8 +80,6 @@ namespace JSC {
     typedef HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> IdentifierSet;
 
     namespace DeclarationStacks {
-        enum VarAttrs { IsConstant = 1, HasInitializer = 2 };
-        typedef Vector<std::pair<Identifier, unsigned>> VarStack;
         typedef Vector<FunctionBodyNode*> FunctionStack;
     }
 
@@ -90,6 +89,8 @@ namespace JSC {
         SwitchType switchType;
     };
 
+    enum class AssignmentContext { DeclarationStatement, AssignmentExpression };
+
     class ParserArenaFreeable {
     public:
         // ParserArenaFreeable objects are are freed when the arena is deleted.
@@ -202,6 +203,20 @@ namespace JSC {
         int m_lastLine;
     };
 
+    class VariableEnvironmentNode {
+    public:
+        VariableEnvironmentNode()
+        {
+        }
+
+        VariableEnvironmentNode(VariableEnvironment& lexicalDeclaredVariables);
+
+        VariableEnvironment& lexicalVariables() { return m_lexicalVariables; }
+
+    protected:
+        VariableEnvironment m_lexicalVariables;
+    };
+
     class ConstantNode : public ExpressionNode {
     public:
         ConstantNode(const JSTokenLocation&, ResultType);
@@ -1149,13 +1164,14 @@ namespace JSC {
 
     class AssignResolveNode : public ExpressionNode, public ThrowableExpressionData {
     public:
-        AssignResolveNode(const JSTokenLocation&, const Identifier&, ExpressionNode* right);
+        AssignResolveNode(const JSTokenLocation&, const Identifier&, ExpressionNode* right, AssignmentContext);
 
     private:
         virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
 
         const Identifier& m_ident;
         ExpressionNode* m_right;
+        AssignmentContext m_assignmentContext;
     };
 
     class ReadModifyBracketNode : public ExpressionNode, public ThrowableSubExpressionData {
@@ -1283,9 +1299,9 @@ namespace JSC {
         StatementNode* m_tail;
     };
 
-    class BlockNode : public StatementNode {
+    class BlockNode : public StatementNode, public VariableEnvironmentNode {
     public:
-        BlockNode(const JSTokenLocation&, SourceElements* = 0);
+        BlockNode(const JSTokenLocation&, SourceElements*, VariableEnvironment&);
 
         StatementNode* singleStatement() const;
         StatementNode* lastStatement() const;
@@ -1330,9 +1346,9 @@ namespace JSC {
         ExpressionNode* m_expr;
     };
 
-    class VarStatementNode : public StatementNode {
+    class DeclarationStatement : public StatementNode {
     public:
-        VarStatementNode(const JSTokenLocation&, ExpressionNode*);
+        DeclarationStatement(const JSTokenLocation&, ExpressionNode*);
     private:
         virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
 
@@ -1349,6 +1365,15 @@ namespace JSC {
         const Identifier& m_ident;
     };
 
+    class EmptyLetExpression : public ExpressionNode {
+    public:
+        EmptyLetExpression(const JSTokenLocation&, const Identifier&);
+
+    private:
+        virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+
+        const Identifier& m_ident;
+    };
 
     class IfElseNode : public StatementNode {
     public:
@@ -1386,9 +1411,9 @@ namespace JSC {
         StatementNode* m_statement;
     };
 
-    class ForNode : public StatementNode {
+    class ForNode : public StatementNode, public VariableEnvironmentNode {
     public:
-        ForNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, ExpressionNode* expr3, StatementNode*);
+        ForNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, ExpressionNode* expr3, StatementNode*, VariableEnvironment&);
 
     private:
         virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
@@ -1401,9 +1426,9 @@ namespace JSC {
     
     class DestructuringPatternNode;
     
-    class EnumerationNode : public StatementNode, public ThrowableExpressionData {
+    class EnumerationNode : public StatementNode, public ThrowableExpressionData, public VariableEnvironmentNode {
     public:
-        EnumerationNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*);
+        EnumerationNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*, VariableEnvironment&);
         
     protected:
         ExpressionNode* m_lexpr;
@@ -1413,7 +1438,7 @@ namespace JSC {
     
     class ForInNode : public EnumerationNode {
     public:
-        ForInNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*);
+        ForInNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*, VariableEnvironment&);
 
     private:
         RegisterID* tryGetBoundLocal(BytecodeGenerator&);
@@ -1425,7 +1450,7 @@ namespace JSC {
     
     class ForOfNode : public EnumerationNode {
     public:
-        ForOfNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*);
+        ForOfNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*, VariableEnvironment&);
         
     private:
         virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
@@ -1529,13 +1554,12 @@ namespace JSC {
         ParameterNode* m_next;
     };
 
-    class ScopeNode : public StatementNode, public ParserArenaRoot {
+    class ScopeNode : public StatementNode, public ParserArenaRoot, public VariableEnvironmentNode {
     public:
-        typedef DeclarationStacks::VarStack VarStack;
         typedef DeclarationStacks::FunctionStack FunctionStack;
 
         ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, bool inStrictContext);
-        ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, const SourceCode&, SourceElements*, VarStack&, FunctionStack&, IdentifierSet&, CodeFeatures, int numConstants);
+        ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, const SourceCode&, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, CodeFeatures, int numConstants);
 
         using ParserArenaRoot::operator new;
 
@@ -1559,13 +1583,11 @@ namespace JSC {
         bool usesThis() const { return m_features & ThisFeature; }
         bool needsActivationForMoreThanVariables() const { return m_features & (EvalFeature | WithFeature | CatchFeature); }
         bool needsActivation() const { return (hasCapturedVariables()) || (m_features & (EvalFeature | WithFeature | CatchFeature)); }
-        bool hasCapturedVariables() const { return !!m_capturedVariables.size(); }
-        size_t capturedVariableCount() const { return m_capturedVariables.size(); }
-        const IdentifierSet& capturedVariables() const { return m_capturedVariables; }
-        bool captures(UniquedStringImpl* uid) { return m_capturedVariables.contains(uid); }
+        bool hasCapturedVariables() const { return m_varDeclarations.hasCapturedVariables(); }
+        bool captures(UniquedStringImpl* uid) { return m_varDeclarations.captures(uid); }
         bool captures(const Identifier& ident) { return captures(ident.impl()); }
 
-        VarStack& varStack() { return m_varStack; }
+        VariableEnvironment& varDeclarations() { return m_varDeclarations; }
         FunctionStack& functionStack() { return m_functionStack; }
 
         int neededConstants()
@@ -1589,16 +1611,15 @@ namespace JSC {
     private:
         CodeFeatures m_features;
         SourceCode m_source;
-        VarStack m_varStack;
+        VariableEnvironment m_varDeclarations;
         FunctionStack m_functionStack;
         int m_numConstants;
         SourceElements* m_statements;
-        IdentifierSet m_capturedVariables;
     };
 
     class ProgramNode : public ScopeNode {
     public:
-        ProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VarStack&, FunctionStack&, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants);
+        ProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, const SourceCode&, CodeFeatures, int numConstants);
 
         unsigned startColumn() const { return m_startColumn; }
         unsigned endColumn() const { return m_endColumn; }
@@ -1617,7 +1638,7 @@ namespace JSC {
 
     class EvalNode : public ScopeNode {
     public:
-        EvalNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VarStack&, FunctionStack&, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants);
+        EvalNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, const SourceCode&, CodeFeatures, int numConstants);
 
         ALWAYS_INLINE unsigned startColumn() const { return 0; }
         unsigned endColumn() const { return m_endColumn; }
@@ -1704,7 +1725,7 @@ namespace JSC {
 
     class FunctionNode final : public ScopeNode {
     public:
-        FunctionNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VarStack&, FunctionStack&, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants);
+        FunctionNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, const SourceCode&, CodeFeatures, int numConstants);
 
         FunctionParameters* parameters() const { return m_parameters.get(); }
 
@@ -1833,14 +1854,14 @@ namespace JSC {
 
     class BindingNode : public DestructuringPatternNode {
     public:
-        static Ref<BindingNode> create(const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end);
+        static Ref<BindingNode> create(const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end, AssignmentContext);
         const Identifier& boundProperty() const { return m_boundProperty; }
 
         const JSTextPosition& divotStart() const { return m_divotStart; }
         const JSTextPosition& divotEnd() const { return m_divotEnd; }
         
     private:
-        BindingNode(const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end);
+        BindingNode(const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end, AssignmentContext);
 
         virtual void collectBoundIdentifiers(Vector<Identifier>&) const override;
         virtual void bindValue(BytecodeGenerator&, RegisterID*) const override;
@@ -1851,6 +1872,7 @@ namespace JSC {
         JSTextPosition m_divotStart;
         JSTextPosition m_divotEnd;
         Identifier m_boundProperty;
+        AssignmentContext m_bindingContext;
     };
 
     class DestructuringAssignmentNode : public ExpressionNode, public ParserArenaDeletable {
@@ -1936,9 +1958,9 @@ namespace JSC {
         ClauseListNode* m_list2;
     };
 
-    class SwitchNode : public StatementNode {
+    class SwitchNode : public StatementNode, public VariableEnvironmentNode {
     public:
-        SwitchNode(const JSTokenLocation&, ExpressionNode*, CaseBlockNode*);
+        SwitchNode(const JSTokenLocation&, ExpressionNode*, CaseBlockNode*, VariableEnvironment&);
 
     private:
         virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
index be88dd2..92ae9ab 100644 (file)
@@ -264,6 +264,8 @@ String Parser<LexerType>::parseInner()
     if (m_lexer->isReparsing())
         m_statementDepth--;
     ScopeRef scope = currentScope();
+    scope->setIsLexicalScope();
+
     SourceElements* sourceElements = parseSourceElements(context, CheckForStrictMode, StandardFunctionParseType);
     if (!sourceElements || !consume(EOFTOK)) {
         if (hasError())
@@ -275,7 +277,10 @@ String Parser<LexerType>::parseInner()
     IdentifierSet capturedVariables;
     bool modifiedParameter = false;
     bool modifiedArguments = false;
-    scope->getCapturedVariables(capturedVariables, modifiedParameter, modifiedArguments);
+    scope->getCapturedVars(capturedVariables, modifiedParameter, modifiedArguments);
+    VariableEnvironment& varDeclarations = scope->declaredVariables();
+    for (auto& entry : capturedVariables)
+        varDeclarations.markVariableAsCaptured(entry);
     
     CodeFeatures features = context.features();
     if (scope->strictMode())
@@ -290,6 +295,7 @@ String Parser<LexerType>::parseInner()
     if (m_parsingBuiltin) {
         IdentifierSet usedVariables;
         scope->getUsedVariables(usedVariables);
+        // FIXME: This needs to be changed if we want to allow builtins to use lexical declarations.
         for (const auto& variable : usedVariables) {
             Identifier identifier = Identifier::fromUid(m_vm, variable.get());
             if (scope->hasDeclaredVariable(identifier))
@@ -306,31 +312,28 @@ String Parser<LexerType>::parseInner()
 
         if (!capturedVariables.isEmpty()) {
             for (const auto& capturedVariable : capturedVariables) {
-                Identifier identifier = Identifier::fromUid(m_vm, capturedVariable.get());
-                if (scope->hasDeclaredVariable(identifier))
+                if (scope->hasDeclaredVariable(capturedVariable))
                     continue;
 
-                if (scope->hasDeclaredParameter(identifier))
+                if (scope->hasDeclaredParameter(capturedVariable))
                     continue;
 
                 RELEASE_ASSERT_NOT_REACHED();
             }
         }
     }
-    didFinishParsing(sourceElements, context.varDeclarations(), context.funcDeclarations(), features,
-        context.numConstants(), capturedVariables, WTF::move(closedVariables));
+    didFinishParsing(sourceElements, context.funcDeclarations(), varDeclarations, features, context.numConstants(), WTF::move(closedVariables));
 
     return parseError;
 }
 
 template <typename LexerType>
-void Parser<LexerType>::didFinishParsing(SourceElements* sourceElements, DeclarationStacks::VarStack& varStack, 
-    DeclarationStacks::FunctionStack& funcStack, CodeFeatures features, int numConstants, IdentifierSet& capturedVars, const Vector<RefPtr<UniquedStringImpl>>&& closedVariables)
+void Parser<LexerType>::didFinishParsing(SourceElements* sourceElements, DeclarationStacks::FunctionStack& funcStack, 
+    VariableEnvironment& varDeclarations, CodeFeatures features, int numConstants, const Vector<RefPtr<UniquedStringImpl>>&& closedVariables)
 {
     m_sourceElements = sourceElements;
-    m_varDeclarations.swap(varStack);
     m_funcDeclarations.swap(funcStack);
-    m_capturedVariables.swap(capturedVars);
+    m_varDeclarations.swap(varDeclarations);
     m_closedVariables = closedVariables;
     m_features = features;
     m_numConstants = numConstants;
@@ -407,30 +410,52 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
 {
     // The grammar is documented here:
     // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-statements
+    DepthManager statementDepth(&m_statementDepth);
+    m_statementDepth++;
     TreeStatement result = 0;
+    bool shouldSetEndOffset = true;
     switch (m_token.m_type) {
     case CONSTTOKEN:
         result = parseConstDeclaration(context);
         break;
+    case LET: {
+        bool shouldParseVariableDeclaration = true;
+        if (!strictMode()) {
+            SavePoint savePoint = createSavePoint();
+            next();
+            if (!match(IDENT) && !match(OPENBRACE) && !match(OPENBRACKET))
+                shouldParseVariableDeclaration = false;
+            restoreSavePoint(savePoint);
+        }
+        if (shouldParseVariableDeclaration)
+            result = parseVariableDeclaration(context, DeclarationType::LexicalDeclaration);
+        else
+            result = parseExpressionOrLabelStatement(context); // Treat this as an IDENT. This is how ::parseStatement() handles IDENT.
+
+        break;
+    }
 #if ENABLE(ES6_CLASS_SYNTAX)
     case CLASSTOKEN:
         result = parseClassDeclaration(context);
         break;
 #endif
     default:
-        // FIXME: This needs to consider 'let' in bug:
-        // https://bugs.webkit.org/show_bug.cgi?id=142944
+        m_statementDepth--; // parseStatement() increments the depth.
         result = parseStatement(context, directive, directiveLiteralLength);
+        shouldSetEndOffset = false;
         break;
     }
 
+    if (result && shouldSetEndOffset)
+        context.setEndOffset(result, m_lastTokenEndPosition.offset);
+
     return result;
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeStatement Parser<LexerType>::parseVarDeclaration(TreeBuilder& context)
+template <class TreeBuilder> TreeStatement Parser<LexerType>::parseVariableDeclaration(TreeBuilder& context, DeclarationType declarationType)
 {
-    ASSERT(match(VAR));
+    ASSERT(match(VAR) || match(LET));
     JSTokenLocation location(tokenLocation());
     int start = tokenLine();
     int end = 0;
@@ -438,11 +463,14 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseVarDeclaratio
     TreeDestructuringPattern scratch1 = 0;
     TreeExpression scratch2 = 0;
     JSTextPosition scratch3;
-    TreeExpression varDecls = parseVarDeclarationList(context, scratch, scratch1, scratch2, scratch3, scratch3, scratch3, VarDeclarationContext);
+    TreeExpression variableDecls = parseVariableDeclarationList(context, scratch, scratch1, scratch2, scratch3, scratch3, scratch3, VarDeclarationContext, declarationType);
     propagateError();
     failIfFalse(autoSemiColon(), "Expected ';' after var declaration");
     
-    return context.createVarStatement(location, varDecls, start, end);
+    if (declarationType == DeclarationType::VarDeclaration)
+        return context.createVarStatement(location, variableDecls, start, end);
+    ASSERT(declarationType == DeclarationType::LexicalDeclaration);
+    return context.createLetStatement(location, variableDecls, start, end);
 }
 
 template <typename LexerType>
@@ -507,8 +535,9 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseWhileStatemen
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVarDeclarationList(TreeBuilder& context, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext declarationListContext)
+template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVariableDeclarationList(TreeBuilder& context, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext declarationListContext, DeclarationType declarationType)
 {
+    ASSERT(declarationType == DeclarationType::LexicalDeclaration || declarationType == DeclarationType::VarDeclaration);
     TreeExpression head = 0;
     TreeExpression tail = 0;
     const Identifier* lastIdent;
@@ -521,7 +550,8 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVarDeclarati
         TreeExpression node = 0;
         declarations++;
         bool hasInitializer = false;
-        if (match(IDENT)) {
+        if (match(IDENT) || isLETMaskedAsIDENT()) {
+            failIfTrue(isLETMaskedAsIDENT() && declarationType == DeclarationType::LexicalDeclaration, "Can't use 'let' as an identifier name for a LexicalDeclaration");
             JSTextPosition varStart = tokenStartPosition();
             JSTokenLocation varStartLocation(tokenLocation());
             identStart = varStart;
@@ -530,8 +560,12 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVarDeclarati
             lastIdentToken = m_token;
             next();
             hasInitializer = match(EQUAL);
-            failIfFalseIfStrict(declareVariable(name), "Cannot declare a variable named ", name->impl(), " in strict mode");
-            context.addVar(name, (hasInitializer || (!m_allowsIn && (match(INTOKEN) || isofToken()))) ? DeclarationStacks::HasInitializer : 0);
+            if (!declareVariable(name, declarationType)) {
+                if (declarationType == DeclarationType::LexicalDeclaration)
+                    internalFailWithMessage(false, "Cannot declare a lexical variable twice: '", name->impl(), "'");
+                else if (strictMode())
+                    internalFailWithMessage(false, "Cannot declare a variable named ", name->impl(), " in strict mode");
+            }
             if (hasInitializer) {
                 JSTextPosition varDivot = tokenStartPosition() + 1;
                 initStart = tokenStartPosition();
@@ -541,12 +575,16 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVarDeclarati
                 lastInitializer = initializer;
                 failIfFalse(initializer, "Expected expression as the intializer for the variable '", name->impl(), "'");
                 
-                node = context.createAssignResolve(location, *name, initializer, varStart, varDivot, lastTokenEndPosition());
-            } else
-                node = context.createEmptyVarExpression(varStartLocation, *name);
+                node = context.createAssignResolve(location, *name, initializer, varStart, varDivot, lastTokenEndPosition(), AssignmentContext::DeclarationStatement);
+            } else {
+                if (declarationType == DeclarationType::VarDeclaration)
+                    node = context.createEmptyVarExpression(varStartLocation, *name);
+                else
+                    node = context.createEmptyLetExpression(varStartLocation, *name);
+            }
         } else {
             lastIdent = 0;
-            auto pattern = parseDestructuringPattern(context, DestructureToVariables);
+            auto pattern = parseDestructuringPattern(context, declarationType == DeclarationType::VarDeclaration ? DestructureToVariables : DestructureToLexicalVariables, AssignmentContext::DeclarationStatement);
             failIfFalse(pattern, "Cannot parse this destructuring pattern");
             hasInitializer = match(EQUAL);
             failIfTrue(declarationListContext == VarDeclarationContext && !hasInitializer, "Expected an initializer in destructuring variable declaration");
@@ -568,20 +606,23 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVarDeclarati
             tail = context.appendToCommaExpr(location, head, tail, node);
     } while (match(COMMA));
     if (lastIdent)
-        lastPattern = createBindingPattern(context, DestructureToVariables, *lastIdent, 0, lastIdentToken);
+        lastPattern = context.createBindingLocation(lastIdentToken.m_location, *lastIdent, lastIdentToken.m_startPosition, lastIdentToken.m_endPosition, AssignmentContext::DeclarationStatement);
+
     return head;
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DestructuringKind kind, const Identifier& name, int depth, JSToken token)
+template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DestructuringKind kind, const Identifier& name, int depth, JSToken token, AssignmentContext bindingContext)
 {
     ASSERT(!name.isNull());
     
     ASSERT(name.impl()->isAtomic() || name.impl()->isSymbol());
     if (depth) {
         if (kind == DestructureToVariables)
-            failIfFalseIfStrict(declareVariable(&name), "Cannot destructure to a variable named '", name.impl(), "' in strict mode");
-        if (kind == DestructureToParameters) {
+            failIfFalseIfStrict(declareVariable(&name), "Cannot deconstruct to a variable named '", name.impl(), "' in strict mode");
+        else if (kind == DestructureToLexicalVariables)
+            semanticFailIfFalse(declareVariable(&name, DeclarationType::LexicalDeclaration), "Cannot declare a lexical variable twice: '", name.impl(), "'");
+        else if (kind == DestructureToParameters) {
             auto bindingResult = declareBoundParameter(&name);
             if (bindingResult == Scope::StrictBindingFailed && strictMode()) {
                 semanticFailIfTrue(m_vm->propertyNames->arguments == name || m_vm->propertyNames->eval == name, "Cannot destructure to a parameter name '", name.impl(), "' in strict mode");
@@ -599,16 +640,13 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createB
                 semanticFail("Cannot destructure to a parameter named '", name.impl(), "'");
             }
         }
-        if (kind != DestructureToExpressions)
-            context.addVar(&name, DeclarationStacks::HasInitializer);
 
     } else {
-        if (kind == DestructureToVariables) {
+        if (kind == DestructureToVariables)
             failIfFalseIfStrict(declareVariable(&name), "Cannot declare a variable named '", name.impl(), "' in strict mode");
-            context.addVar(&name, DeclarationStacks::HasInitializer);
-        }
-        
-        if (kind == DestructureToParameters) {
+        else if (kind == DestructureToLexicalVariables)
+            semanticFailIfFalse(declareVariable(&name, DeclarationType::LexicalDeclaration), "Cannot declare a lexical variable twice: '", name.impl(), "'");
+        else if (kind == DestructureToParameters) {
             bool declarationResult = declareParameter(&name);
             if (!declarationResult && strictMode()) {
                 semanticFailIfTrue(m_vm->propertyNames->arguments == name || m_vm->propertyNames->eval == name, "Cannot destructure to a parameter name '", name.impl(), "' in strict mode");
@@ -621,7 +659,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createB
             }
         }
     }
-    return context.createBindingLocation(token.m_location, name, token.m_startPosition, token.m_endPosition);
+    return context.createBindingLocation(token.m_location, name, token.m_startPosition, token.m_endPosition, bindingContext);
 }
 
 #if ENABLE(ES6_ARROWFUNCTION_SYNTAX)
@@ -661,13 +699,13 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseArrowFunction
 #endif
 
 template <typename LexerType>
-template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::tryParseDestructuringPatternExpression(TreeBuilder& context)
+template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::tryParseDestructuringPatternExpression(TreeBuilder& context, AssignmentContext bindingContext)
 {
-    return parseDestructuringPattern(context, DestructureToExpressions);
+    return parseDestructuringPattern(context, DestructureToExpressions, bindingContext);
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDestructuringPattern(TreeBuilder& context, DestructuringKind kind, int depth)
+template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDestructuringPattern(TreeBuilder& context, DestructuringKind kind, AssignmentContext bindingContext, int depth)
 {
     failIfStackOverflow();
     int nonLHSCount = m_nonLHSCount;
@@ -693,7 +731,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
             if (UNLIKELY(match(DOTDOTDOT))) {
                 JSTokenLocation location = m_token.m_location;
                 next();
-                auto innerPattern = parseDestructuringPattern(context, kind, depth + 1);
+                auto innerPattern = parseDestructuringPattern(context, kind, bindingContext, depth + 1);
                 if (kind == DestructureToExpressions && !innerPattern)
                     return 0;
                 failIfFalse(innerPattern, "Cannot parse this destructuring pattern");
@@ -706,7 +744,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
             }
 
             JSTokenLocation location = m_token.m_location;
-            auto innerPattern = parseDestructuringPattern(context, kind, depth + 1);
+            auto innerPattern = parseDestructuringPattern(context, kind, bindingContext, depth + 1);
             if (kind == DestructureToExpressions && !innerPattern)
                 return 0;
             failIfFalse(innerPattern, "Cannot parse this destructuring pattern");
@@ -735,14 +773,15 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
             Identifier propertyName;
             TreeDestructuringPattern innerPattern = 0;
             JSTokenLocation location = m_token.m_location;
-            if (match(IDENT)) {
+            if (match(IDENT) || isLETMaskedAsIDENT()) {
+                failIfTrue(isLETMaskedAsIDENT() && kind == DestructureToLexicalVariables, "Can't use 'let' as an identifier name for a LexicalDeclaration");
                 propertyName = *m_token.m_data.ident;
                 JSToken identifierToken = m_token;
                 next();
                 if (consume(COLON))
-                    innerPattern = parseDestructuringPattern(context, kind, depth + 1);
+                    innerPattern = parseDestructuringPattern(context, kind, bindingContext, depth + 1);
                 else
-                    innerPattern = createBindingPattern(context, kind, propertyName, depth, identifierToken);
+                    innerPattern = createBindingPattern(context, kind, propertyName, depth, identifierToken, bindingContext);
             } else {
                 JSTokenType tokenType = m_token.m_type;
                 switch (m_token.m_type) {
@@ -773,7 +812,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
                     
                     failWithMessage("Expected a ':' prior to a named destructuring property");
                 }
-                innerPattern = parseDestructuringPattern(context, kind, depth + 1);
+                innerPattern = parseDestructuringPattern(context, kind, bindingContext, depth + 1);
             }
             if (kind == DestructureToExpressions && !innerPattern)
                 return 0;
@@ -791,13 +830,14 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
     }
 
     default: {
-        if (!match(IDENT)) {
+        if (!match(IDENT) && !isLETMaskedAsIDENT()) {
             if (kind == DestructureToExpressions)
                 return 0;
             semanticFailureDueToKeyword("variable name");
             failWithMessage("Expected a parameter pattern or a ')' in parameter list");
         }
-        pattern = createBindingPattern(context, kind, *m_token.m_data.ident, depth, m_token);
+        failIfTrue(isLETMaskedAsIDENT() && kind == DestructureToLexicalVariables, "Can't use 'let' as an identifier name for a LexicalDeclaration");
+        pattern = createBindingPattern(context, kind, *m_token.m_data.ident, depth, m_token, bindingContext);
         next();
         break;
     }
@@ -825,13 +865,12 @@ template <class TreeBuilder> TreeConstDeclList Parser<LexerType>::parseConstDecl
     do {
         JSTokenLocation location(tokenLocation());
         next();
-        matchOrFail(IDENT, "Expected an identifier name in const declaration");
+        failIfFalse(match(IDENT), "Expected an identifier name in const declaration");
         const Identifier* name = m_token.m_data.ident;
         next();
         bool hasInitializer = match(EQUAL);
-        declareVariable(name);
-        context.addVar(name, DeclarationStacks::IsConstant | (hasInitializer ? DeclarationStacks::HasInitializer : 0));
-
+        // FIXME: This should be LexicalVariable when we support proper ES6 const semantics.
+        declareVariable(name, DeclarationType::VarDeclaration, true);
         TreeExpression initializer = 0;
         if (hasInitializer) {
             next(TreeBuilder::DontBuildStrings); // consume '='
@@ -859,17 +898,44 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement(
     JSTextPosition declsEnd;
     TreeExpression decls = 0;
     TreeDestructuringPattern pattern = 0;
-    if (match(VAR)) {
+    bool isVarDeclaraton = match(VAR);
+    bool isLetDeclaration = match(LET);
+
+    VariableEnvironment dummySet;
+    VariableEnvironment* lexicalVariables = nullptr;
+    AutoCleanupLexicalScope lexicalScope;
+
+    auto gatherLexicalVariablesIfNecessary = [&] {
+        if (isLetDeclaration) {
+            ScopeRef scope = lexicalScope.scope();
+            lexicalVariables = &scope->finalizeLexicalEnvironment();
+        } else
+            lexicalVariables = &dummySet;
+    };
+
+    auto popLexicalScopeIfNecessary = [&] {
+        if (isLetDeclaration)
+            popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo);
+    };
+
+    if (isVarDeclaraton || isLetDeclaration) {
         /*
-         for (var IDENT in expression) statement
-         for (var varDeclarationList; expressionOpt; expressionOpt)
+         for (var/let IDENT in expression) statement
+         for (var/let varDeclarationList; expressionOpt; expressionOpt)
          */
+        if (isLetDeclaration) {
+            ScopeRef newScope = pushScope();
+            newScope->setIsLexicalScope();
+            newScope->preventVarDeclarations();
+            lexicalScope.setIsValid(newScope, this);
+        }
+
         TreeDestructuringPattern forInTarget = 0;
         TreeExpression forInInitializer = 0;
         m_allowsIn = false;
         JSTextPosition initStart;
         JSTextPosition initEnd;
-        decls = parseVarDeclarationList(context, declarations, forInTarget, forInInitializer, declsStart, initStart, initEnd, ForLoopContext);
+        decls = parseVariableDeclarationList(context, declarations, forInTarget, forInInitializer, declsStart, initStart, initEnd, ForLoopContext, isVarDeclaraton ? DeclarationType::VarDeclaration : DeclarationType::LexicalDeclaration);
         m_allowsIn = true;
         propagateError();
 
@@ -905,16 +971,21 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement(
         TreeStatement statement = parseStatement(context, unused);
         endLoop();
         failIfFalse(statement, "Expected statement as body of for-", isOfEnumeration ? "of" : "in", " statement");
+        gatherLexicalVariablesIfNecessary();
+        TreeStatement result;
         if (isOfEnumeration)
-            return context.createForOfLoop(location, forInTarget, expr, statement, declsStart, inLocation, exprEnd, startLine, endLine);
-        return context.createForInLoop(location, forInTarget, expr, statement, declsStart, inLocation, exprEnd, startLine, endLine);
+            result = context.createForOfLoop(location, forInTarget, expr, statement, declsStart, inLocation, exprEnd, startLine, endLine, *lexicalVariables);
+        else
+            result = context.createForInLoop(location, forInTarget, expr, statement, declsStart, inLocation, exprEnd, startLine, endLine, *lexicalVariables);
+        popLexicalScopeIfNecessary();
+        return result;
     }
     
     if (!match(SEMICOLON)) {
         if (match(OPENBRACE) || match(OPENBRACKET)) {
             SavePoint savePoint = createSavePoint();
             declsStart = tokenStartPosition();
-            pattern = tryParseDestructuringPatternExpression(context);
+            pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::DeclarationStatement);
             declsEnd = lastTokenEndPosition();
             if (pattern && (match(INTOKEN) || (match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->of)))
                 goto enumerationLoop;
@@ -953,10 +1024,13 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement(
         TreeStatement statement = parseStatement(context, unused);
         endLoop();
         failIfFalse(statement, "Expected a statement as the body of a for loop");
-        return context.createForLoop(location, decls, condition, increment, statement, startLine, endLine);
+        gatherLexicalVariablesIfNecessary();
+        TreeStatement result = context.createForLoop(location, decls, condition, increment, statement, startLine, endLine, *lexicalVariables);
+        popLexicalScopeIfNecessary();
+        return result;
     }
     
-    // For-in loop
+    // For-in and For-of loop
 enumerationLoop:
     failIfFalse(nonLHSCount == m_nonLHSCount, "Expected a reference on the left hand side of an enumeration statement");
     bool isOfEnumeration = false;
@@ -976,15 +1050,24 @@ enumerationLoop:
     TreeStatement statement = parseStatement(context, unused);
     endLoop();
     failIfFalse(statement, "Expected a statement as the body of a for-", isOfEnumeration ? "of" : "in", "loop");
+    gatherLexicalVariablesIfNecessary();
+    TreeStatement result;
     if (pattern) {
         ASSERT(!decls);
         if (isOfEnumeration)
-            return context.createForOfLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine);
-        return context.createForInLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine);
+            result = context.createForOfLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables);
+        else 
+            result = context.createForInLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables);
+
+        popLexicalScopeIfNecessary();
+        return result;
     }
     if (isOfEnumeration)
-        return context.createForOfLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine);
-    return context.createForInLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine);
+        result = context.createForOfLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables);
+    else
+        result = context.createForInLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables);
+    popLexicalScopeIfNecessary();
+    return result;
 }
 
 template <typename LexerType>
@@ -1000,7 +1083,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseBreakStatemen
         semanticFailIfFalse(breakIsValid(), "'break' is only valid inside a switch or loop statement");
         return context.createBreakStatement(location, &m_vm->propertyNames->nullIdentifier, start, end);
     }
-    matchOrFail(IDENT, "Expected an identifier as the target for a break statement");
+    failIfFalse(match(IDENT) || isLETMaskedAsIDENT(), "Expected an identifier as the target for a break statement");
     const Identifier* ident = m_token.m_data.ident;
     semanticFailIfFalse(getLabel(ident), "Cannot use the undeclared label '", ident->impl(), "'");
     end = tokenEndPosition();
@@ -1022,7 +1105,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseContinueState
         semanticFailIfFalse(continueIsValid(), "'continue' is only valid inside a loop statement");
         return context.createContinueStatement(location, &m_vm->propertyNames->nullIdentifier, start, end);
     }
-    matchOrFail(IDENT, "Expected an identifier as the target for a continue statement");
+    failIfFalse(match(IDENT) || isLETMaskedAsIDENT(), "Expected an identifier as the target for a continue statement");
     const Identifier* ident = m_token.m_data.ident;
     ScopeLabelInfo* label = getLabel(ident);
     semanticFailIfFalse(label, "Cannot use the undeclared label '", ident->impl(), "'");
@@ -1116,6 +1199,9 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseSwitchStateme
     
     handleProductionOrFail(CLOSEPAREN, ")", "end", "subject of a 'switch'");
     handleProductionOrFail(OPENBRACE, "{", "start", "body of a 'switch'");
+    AutoPopScopeRef lexicalScope(this, pushScope());
+    lexicalScope->setIsLexicalScope();
+    lexicalScope->preventVarDeclarations();
     startSwitch();
     TreeClauseList firstClauses = parseSwitchClauses(context);
     propagateError();
@@ -1128,8 +1214,9 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseSwitchStateme
     endSwitch();
     handleProductionOrFail(CLOSEBRACE, "}", "end", "body of a 'switch'");
     
-    return context.createSwitchStatement(location, expr, firstClauses, defaultClause, secondClauses, startLine, endLine);
-    
+    TreeStatement result = context.createSwitchStatement(location, expr, firstClauses, defaultClause, secondClauses, startLine, endLine, lexicalScope->finalizeLexicalEnvironment());
+    popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo);
+    return result;
 }
 
 template <typename LexerType>
@@ -1201,7 +1288,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseTryStatement(
         next();
         
         handleProductionOrFail(OPENPAREN, "(", "start", "'catch' target");
-        if (!match(IDENT)) {
+        if (!(match(IDENT) || isLETMaskedAsIDENT())) {
             semanticFailureDueToKeyword("catch variable name");
             failWithMessage("Expected identifier name as catch target");
         }
@@ -1209,12 +1296,12 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseTryStatement(
         next();
         AutoPopScopeRef catchScope(this, pushScope());
         failIfFalseIfStrict(declareVariable(ident), "Cannot declare a catch variable named '", ident->impl(), "' in strict mode");
-        catchScope->preventNewDecls();
+        catchScope->preventAllVariableDeclarations();
         handleProductionOrFail(CLOSEPAREN, ")", "end", "'catch' target");
         matchOrFail(OPENBRACE, "Expected exception handler to be a block statement");
         catchBlock = parseBlockStatement(context);
         failIfFalse(catchBlock, "Unable to parse 'catch' block");
-        failIfFalse(popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo), "Parse error");
+        popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo);
     }
     
     if (match(FINALLY)) {
@@ -1245,16 +1332,30 @@ template <typename LexerType>
 template <class TreeBuilder> TreeStatement Parser<LexerType>::parseBlockStatement(TreeBuilder& context)
 {
     ASSERT(match(OPENBRACE));
+
+    // We should treat the first block statement of the function (the body of the function) as the lexical 
+    // scope of the function itself, and not the lexical scope of a 'block' statement within the function.
+    AutoCleanupLexicalScope lexicalScope;
+    bool shouldPushLexicalScope = m_statementDepth > 0;
+    if (shouldPushLexicalScope) {
+        ScopeRef newScope = pushScope();
+        newScope->setIsLexicalScope();
+        newScope->preventVarDeclarations();
+        lexicalScope.setIsValid(newScope, this);
+    }
     JSTokenLocation location(tokenLocation());
     int startOffset = m_token.m_data.offset;
     int start = tokenLine();
+    VariableEnvironment emptyEnvironment;
     next();
     if (match(CLOSEBRACE)) {
         int endOffset = m_token.m_data.offset;
         next();
-        TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line);
+        TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line, shouldPushLexicalScope ? currentScope()->finalizeLexicalEnvironment() : emptyEnvironment);
         context.setStartOffset(result, startOffset);
         context.setEndOffset(result, endOffset);
+        if (shouldPushLexicalScope)
+            popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo);
         return result;
     }
     TreeSourceElements subtree = parseSourceElements(context, DontCheckForStrictMode, StandardFunctionParseType);
@@ -1262,9 +1363,12 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseBlockStatemen
     matchOrFail(CLOSEBRACE, "Expected a closing '}' at the end of a block statement");
     int endOffset = m_token.m_data.offset;
     next();
-    TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line);
+    TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line, shouldPushLexicalScope ? currentScope()->finalizeLexicalEnvironment() : emptyEnvironment);
     context.setStartOffset(result, startOffset);
     context.setEndOffset(result, endOffset);
+    if (shouldPushLexicalScope)
+        popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo);
+
     return result;
 }
 
@@ -1285,7 +1389,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre
         shouldSetEndOffset = false;
         break;
     case VAR:
-        result = parseVarDeclaration(context);
+        result = parseVariableDeclaration(context, DeclarationType::VarDeclaration);
         break;
     case FUNCTION:
         failIfFalseIfStrict(m_statementDepth == 1, "Strict mode does not allow function declarations in a lexically nested statement");
@@ -1492,7 +1596,7 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
     
     switch (parseType) {
     case StandardFunctionParseType: {
-        if (match(IDENT)) {
+        if (match(IDENT) || isLETMaskedAsIDENT()) {
             info.name = m_token.m_data.ident;
             m_lastFunctionName = info.name;
             next();
@@ -1580,7 +1684,7 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
             cachedInfo->strictMode, constructorKind);
         
         functionScope->restoreFromSourceProviderCache(cachedInfo);
-        failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo), "Parser error");
+        popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo);
         
         m_token = cachedInfo->endFunctionToken();
         
@@ -1688,7 +1792,7 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
         newInfo = SourceProviderCacheItem::create(parameters);
     }
     
-    failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo), "Parser error");
+    popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo);
     
 #if ENABLE(ES6_ARROWFUNCTION_SYNTAX)
     if (info.functionBodyType == ArrowFunctionBodyExpression)
@@ -1736,10 +1840,8 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseClassDeclarat
     ParserClassInfo<TreeBuilder> info;
     TreeClassExpression classExpr = parseClass(context, FunctionNeedsName, info);
     failIfFalse(classExpr, "Failed to parse class");
-    declareVariable(info.className);
-
     // FIXME: This should be like `let`, not `var`.
-    context.addVar(info.className, DeclarationStacks::HasInitializer);
+    declareVariable(info.className);
 
     JSTextPosition classEnd = lastTokenEndPosition();
     unsigned classEndLine = tokenLine();
@@ -1866,7 +1968,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
         }
     }
 
-    failIfFalse(popScope(classScope, TreeBuilder::NeedsFreeVariableInfo), "Parser error");
+    popScope(classScope, TreeBuilder::NeedsFreeVariableInfo);
     consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body");
 
     return context.createClassExpr(location, *className, constructor, parentClass, instanceMethods, staticMethods);
@@ -1920,7 +2022,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrL
             failIfTrue(getLabel(ident), "Cannot find scope for the label '", ident->impl(), "'");
             labels.append(LabelInfo(ident, start, end));
         }
-    } while (match(IDENT));
+    } while (match(IDENT) || isLETMaskedAsIDENT());
     bool isLoop = false;
     switch (m_token.m_type) {
     case FOR:
@@ -1933,6 +2035,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrL
         break;
     }
     const Identifier* unused = 0;
+    ScopeRef labelScope = currentScope();
     if (!m_syntaxAlreadyValidated) {
         for (size_t i = 0; i < labels.size(); i++)
             pushLabel(labels[i].m_ident, isLoop);
@@ -1940,7 +2043,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrL
     TreeStatement statement = parseStatement(context, unused);
     if (!m_syntaxAlreadyValidated) {
         for (size_t i = 0; i < labels.size(); i++)
-            popLabel();
+            popLabel(labelScope);
     }
     failIfFalse(statement, "Cannot parse statement");
     for (size_t i = 0; i < labels.size(); i++) {
@@ -2102,7 +2205,7 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
     int initialNonLHSCount = m_nonLHSCount;
     if (match(OPENBRACE) || match(OPENBRACKET)) {
         SavePoint savePoint = createSavePoint();
-        auto pattern = tryParseDestructuringPatternExpression(context);
+        auto pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::AssignmentExpression);
         if (pattern && consume(EQUAL)) {
             auto rhs = parseAssignmentExpression(context);
             if (rhs)
@@ -2367,7 +2470,7 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(T
 {
     const Identifier* stringPropertyName = 0;
     double numericPropertyName = 0;
-    if (m_token.m_type == IDENT || m_token.m_type == STRING) {
+    if (m_token.m_type == IDENT || m_token.m_type == STRING || isLETMaskedAsIDENT()) {
         stringPropertyName = m_token.m_data.ident;
         semanticFailIfTrue(superBinding == SuperBinding::Needed && *stringPropertyName == m_vm->propertyNames->prototype,
             "Cannot declare a static method named 'prototype'");
@@ -2683,6 +2786,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
         return context.thisExpr(location, m_thisTDZMode);
     }
     case IDENT: {
+    identifierExpression:
         JSTextPosition start = tokenStartPosition();
         const Identifier* ident = m_token.m_data.ident;
         JSTokenLocation location(tokenLocation());
@@ -2748,6 +2852,10 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
     case TEMPLATE:
         return parseTemplateLiteral(context, LexerType::RawStringsBuildMode::DontBuildRawStrings);
 #endif
+    case LET:
+        if (!strictMode())
+            goto identifierExpression;
+        FALLTHROUGH;
     default:
         failDueToUnexpectedToken();
     }
index a0e5ce7..1ca7601 100644 (file)
@@ -36,6 +36,7 @@
 #include "SourceProvider.h"
 #include "SourceProviderCache.h"
 #include "SourceProviderCacheItem.h"
+#include "VariableEnvironment.h"
 #include <wtf/Forward.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/RefPtr.h>
@@ -97,6 +98,7 @@ enum FunctionParseMode {
 };
 enum DestructuringKind {
     DestructureToVariables,
+    DestructureToLexicalVariables,
     DestructureToParameters,
     DestructureToExpressions
 };
@@ -117,9 +119,11 @@ struct Scope {
         , m_needsFullActivation(false)
         , m_hasDirectSuper(false)
         , m_needsSuperBinding(false)
-        , m_allowsNewDecls(true)
+        , m_allowsVarDeclarations(true)
+        , m_allowsLexicalDeclarations(true)
         , m_strictMode(strictMode)
         , m_isFunction(isFunction)
+        , m_isLexicalScope(false)
         , m_isFunctionBoundary(false)
         , m_isValidStrictMode(true)
         , m_loopDepth(0)
@@ -134,9 +138,11 @@ struct Scope {
         , m_needsFullActivation(rhs.m_needsFullActivation)
         , m_hasDirectSuper(rhs.m_hasDirectSuper)
         , m_needsSuperBinding(rhs.m_needsSuperBinding)
-        , m_allowsNewDecls(rhs.m_allowsNewDecls)
+        , m_allowsVarDeclarations(rhs.m_allowsVarDeclarations)
+        , m_allowsLexicalDeclarations(rhs.m_allowsLexicalDeclarations)
         , m_strictMode(rhs.m_strictMode)
         , m_isFunction(rhs.m_isFunction)
+        , m_isLexicalScope(rhs.m_isLexicalScope)
         , m_isFunctionBoundary(rhs.m_isFunctionBoundary)
         , m_isValidStrictMode(rhs.m_isValidStrictMode)
         , m_loopDepth(rhs.m_loopDepth)
@@ -189,31 +195,106 @@ struct Scope {
     {
         m_isFunction = true;
         m_isFunctionBoundary = true;
+        setIsLexicalScope();
+    }
+
+    bool isFunction() const { return m_isFunction; }
+    bool isFunctionBoundary() const { return m_isFunctionBoundary; }
+
+    void setIsLexicalScope() 
+    { 
+        m_isLexicalScope = true;
+        m_allowsLexicalDeclarations = true;
+    }
+    bool isLexicalScope() { return m_isLexicalScope; }
+
+    VariableEnvironment& declaredVariables() { return m_declaredVariables; }
+    VariableEnvironment& finalizeLexicalEnvironment() 
+    { 
+        if (m_usesEval || m_needsFullActivation)
+            m_lexicalVariables.markAllVariablesAsCaptured();
+        else
+            computeLexicallyCapturedVariablesAndPurgeCandidates();
+
+        return m_lexicalVariables;
+    }
+
+    void computeLexicallyCapturedVariablesAndPurgeCandidates()
+    {
+        // Because variables may be defined at any time in the range of a lexical scope, we must
+        // track lexical variables that might be captured. Then, when we're preparing to pop the top
+        // lexical scope off the stack, we should find which variables are truly captured, and which
+        // variable still may be captured in a parent scope.
+        if (m_lexicalVariables.size() && m_closedVariableCandidates.size()) {
+            auto end = m_closedVariableCandidates.end();
+            for (auto iter = m_closedVariableCandidates.begin(); iter != end; ++iter)
+                m_lexicalVariables.markVariableAsCapturedIfDefined(iter->get());
+        }
+
+        // We can now purge values from the captured candidates because they're captured in this scope.
+        {
+            for (auto entry : m_lexicalVariables) {
+                if (entry.value.isCaptured())
+                    m_closedVariableCandidates.remove(entry.key);
+            }
+        }
     }
-    bool isFunction() { return m_isFunction; }
-    bool isFunctionBoundary() { return m_isFunctionBoundary; }
 
     void declareCallee(const Identifier* ident)
     {
-        m_declaredVariables.add(ident->impl());
+        auto addResult = m_declaredVariables.add(ident->impl());
+        // We want to track if callee is captured, but we don't want to act like it's a 'var'
+        // because that would cause the BytecodeGenerator to emit bad code.
+        addResult.iterator->value.clearIsVar();
     }
 
-    bool declareVariable(const Identifier* ident)
+    bool declareVariable(const Identifier* ident, bool isConstant = false)
     {
+        ASSERT(m_allowsVarDeclarations);
         bool isValidStrictMode = m_vm->propertyNames->eval != *ident && m_vm->propertyNames->arguments != *ident;
         m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
-        m_declaredVariables.add(ident->impl());
+        auto addResult = m_declaredVariables.add(ident->impl());
+        addResult.iterator->value.setIsVar();
+        if (isConstant)
+            addResult.iterator->value.setIsConstant();
+
         return isValidStrictMode;
     }
 
+    bool declareLexicalVariable(const Identifier* ident)
+    {
+        ASSERT(m_allowsLexicalDeclarations);
+        bool isValidStrictMode = m_vm->propertyNames->eval != *ident && m_vm->propertyNames->arguments != *ident;
+        m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
+        auto addResult = m_lexicalVariables.add(ident->impl());
+        addResult.iterator->value.setIsLet();
+        bool successfulDeclaration = addResult.isNewEntry && isValidStrictMode;
+        return successfulDeclaration;
+    }
+
     bool hasDeclaredVariable(const Identifier& ident)
     {
-        return m_declaredVariables.contains(ident.impl());
+        return hasDeclaredVariable(ident.impl());
+    }
+
+    bool hasDeclaredVariable(const RefPtr<UniquedStringImpl>& ident)
+    {
+        return m_declaredVariables.contains(ident.get());
+    }
+
+    bool hasLexicallyDeclaredVariable(const RefPtr<UniquedStringImpl>& ident) const
+    {
+        return m_lexicalVariables.contains(ident.get());
     }
     
-    bool hasDeclaredParameter(const Identifier& ident)
+    ALWAYS_INLINE bool hasDeclaredParameter(const Identifier& ident)
     {
-        return m_declaredParameters.contains(ident.impl()) || m_declaredVariables.contains(ident.impl());
+        return hasDeclaredParameter(ident.impl());
+    }
+
+    bool hasDeclaredParameter(const RefPtr<UniquedStringImpl>& ident)
+    {
+        return m_declaredParameters.contains(ident) || m_declaredVariables.contains(ident.get());
     }
     
     void declareWrite(const Identifier* ident)
@@ -222,13 +303,22 @@ struct Scope {
         m_writtenVariables.add(ident->impl());
     }
 
-    void preventNewDecls() { m_allowsNewDecls = false; }
-    bool allowsNewDecls() const { return m_allowsNewDecls; }
+    void preventAllVariableDeclarations()
+    {
+        m_allowsVarDeclarations = false; 
+        m_allowsLexicalDeclarations = false;
+    }
+    void preventVarDeclarations() { m_allowsVarDeclarations = false; }
+    bool allowsVarDeclarations() const { return m_allowsVarDeclarations; }
+    bool allowsLexicalDeclarations() const { return m_allowsLexicalDeclarations; }
 
     bool declareParameter(const Identifier* ident)
     {
+        ASSERT(m_allowsVarDeclarations);
         bool isArguments = m_vm->propertyNames->arguments == *ident;
-        bool isValidStrictMode = m_declaredVariables.add(ident->impl()).isNewEntry && m_vm->propertyNames->eval != *ident && !isArguments;
+        auto addResult = m_declaredVariables.add(ident->impl());
+        addResult.iterator->value.clearIsVar();
+        bool isValidStrictMode = addResult.isNewEntry && m_vm->propertyNames->eval != *ident && !isArguments;
         m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
         m_declaredParameters.add(ident->impl());
 
@@ -245,13 +335,14 @@ struct Scope {
     BindingResult declareBoundParameter(const Identifier* ident)
     {
         bool isArguments = m_vm->propertyNames->arguments == *ident;
-        bool newEntry = m_declaredVariables.add(ident->impl()).isNewEntry;
-        bool isValidStrictMode = newEntry && m_vm->propertyNames->eval != *ident && !isArguments;
+        auto addResult = m_declaredVariables.add(ident->impl());
+        addResult.iterator->value.setIsVar(); // Treat destructuring parameters as "var"s.
+        bool isValidStrictMode = addResult.isNewEntry && m_vm->propertyNames->eval != *ident && !isArguments;
         m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
     
         if (isArguments)
             m_shadowsArguments = true;
-        if (!newEntry)
+        if (!addResult.isNewEntry)
             return BindingFailed;
         return isValidStrictMode ? BindingSucceeded : StrictBindingFailed;
     }
@@ -268,6 +359,7 @@ struct Scope {
     }
 
     void setNeedsFullActivation() { m_needsFullActivation = true; }
+    bool needsFullActivation() const { return m_needsFullActivation; }
 
 #if ENABLE(ES6_CLASS_SYNTAX)
     bool hasDirectSuper() { return m_hasDirectSuper; }
@@ -283,38 +375,51 @@ struct Scope {
 #endif
     void setNeedsSuperBinding() { m_needsSuperBinding = true; }
 
-    bool collectFreeVariables(Scope* nestedScope, bool shouldTrackClosedVariables)
+    void collectFreeVariables(Scope* nestedScope, bool shouldTrackClosedVariables)
     {
         if (nestedScope->m_usesEval)
             m_usesEval = true;
-        IdentifierSet::iterator end = nestedScope->m_usedVariables.end();
-        for (IdentifierSet::iterator ptr = nestedScope->m_usedVariables.begin(); ptr != end; ++ptr) {
-            if (nestedScope->m_declaredVariables.contains(*ptr))
-                continue;
-            m_usedVariables.add(*ptr);
-            if (shouldTrackClosedVariables)
-                m_closedVariables.add(*ptr);
+
+        {
+            IdentifierSet::iterator end = nestedScope->m_usedVariables.end();
+            for (IdentifierSet::iterator ptr = nestedScope->m_usedVariables.begin(); ptr != end; ++ptr) {
+                if (nestedScope->m_declaredVariables.contains(*ptr) || nestedScope->m_lexicalVariables.contains(*ptr))
+                    continue;
+                m_usedVariables.add(*ptr);
+                // We don't want a declared variable that is used in an inner scope to be thought of as captured if
+                // that inner scope is both a lexical scope and not a function. Only inner functions and "catch" 
+                // statements can cause variables to be captured.
+                if (shouldTrackClosedVariables && (nestedScope->m_isFunctionBoundary || !nestedScope->m_isLexicalScope))
+                    m_closedVariableCandidates.add(*ptr);
+            }
         }
+        // Propagate closed variable candidates downwards within the same function.
+        // Cross function captures will be realized via m_usedVariables propagation.
+        if (shouldTrackClosedVariables && !nestedScope->m_isFunctionBoundary && nestedScope->m_closedVariableCandidates.size()) {
+            IdentifierSet::iterator end = nestedScope->m_closedVariableCandidates.end();
+            IdentifierSet::iterator begin = nestedScope->m_closedVariableCandidates.begin();
+            m_closedVariableCandidates.add(begin, end);
+        }
+
         if (nestedScope->m_writtenVariables.size()) {
             IdentifierSet::iterator end = nestedScope->m_writtenVariables.end();
             for (IdentifierSet::iterator ptr = nestedScope->m_writtenVariables.begin(); ptr != end; ++ptr) {
-                if (nestedScope->m_declaredVariables.contains(*ptr))
+                if (nestedScope->m_declaredVariables.contains(*ptr) || nestedScope->m_lexicalVariables.contains(*ptr))
                     continue;
                 m_writtenVariables.add(*ptr);
             }
         }
-
-        return true;
     }
-
-    void getCapturedVariables(IdentifierSet& capturedVariables, bool& modifiedParameter, bool& modifiedArguments)
+    
+    void getCapturedVars(IdentifierSet& capturedVariables, bool& modifiedParameter, bool& modifiedArguments)
     {
         if (m_needsFullActivation || m_usesEval) {
             modifiedParameter = true;
-            capturedVariables = m_declaredVariables;
+            for (auto& entry : m_declaredVariables)
+                capturedVariables.add(entry.key);
             return;
         }
-        for (IdentifierSet::iterator ptr = m_closedVariables.begin(); ptr != m_closedVariables.end(); ++ptr) {
+        for (IdentifierSet::iterator ptr = m_closedVariableCandidates.begin(); ptr != m_closedVariableCandidates.end(); ++ptr) {
             if (!m_declaredVariables.contains(*ptr))
                 continue;
             capturedVariables.add(*ptr);
@@ -343,7 +448,7 @@ struct Scope {
     {
         IdentifierSet::iterator end = capturedVariables.end();
         for (IdentifierSet::iterator it = capturedVariables.begin(); it != end; ++it) {
-            if (m_declaredVariables.contains(*it))
+            if (m_declaredVariables.contains(*it) || m_lexicalVariables.contains(*it))
                 continue;
             vector.append(*it);
         }
@@ -378,9 +483,11 @@ private:
     bool m_needsFullActivation : 1;
     bool m_hasDirectSuper : 1;
     bool m_needsSuperBinding : 1;
-    bool m_allowsNewDecls : 1;
+    bool m_allowsVarDeclarations : 1;
+    bool m_allowsLexicalDeclarations : 1;
     bool m_strictMode : 1;
     bool m_isFunction : 1;
+    bool m_isLexicalScope : 1;
     bool m_isFunctionBoundary : 1;
     bool m_isValidStrictMode : 1;
     int m_loopDepth;
@@ -389,9 +496,10 @@ private:
     typedef Vector<ScopeLabelInfo, 2> LabelStack;
     std::unique_ptr<LabelStack> m_labels;
     IdentifierSet m_declaredParameters;
-    IdentifierSet m_declaredVariables;
+    VariableEnvironment m_declaredVariables;
+    VariableEnvironment m_lexicalVariables;
     IdentifierSet m_usedVariables;
-    IdentifierSet m_closedVariables;
+    IdentifierSet m_closedVariableCandidates;
     IdentifierSet m_writtenVariables;
 };
 
@@ -479,6 +587,47 @@ private:
         Parser* m_parser;
     };
 
+    struct AutoCleanupLexicalScope {
+        // We can allocate this object on the stack without actually knowing beforehand if we're 
+        // going to create a new lexical scope. If we decide to create a new lexical scope, we
+        // can pass the scope into this obejct and it will take care of the cleanup for us if the parse fails.
+        // This is helpful if we may fail from syntax errors after creating a lexical scope conditionally.
+        AutoCleanupLexicalScope()
+            : m_scope(nullptr, UINT_MAX)
+            , m_parser(nullptr)
+        {
+        }
+
+        ~AutoCleanupLexicalScope()
+        {
+            // This should only ever be called if we fail from a syntax error. Otherwise
+            // it's the intention that a user of this class pops this scope manually on a 
+            // successful parse. 
+            if (isValid())
+                m_parser->popScope(*this, false);
+        }
+
+        void setIsValid(ScopeRef& scope, Parser* parser)
+        {
+            RELEASE_ASSERT(scope->isLexicalScope());
+            m_scope = scope;
+            m_parser = parser;
+        }
+
+        bool isValid() const { return !!m_parser; }
+
+        void setPopped()
+        {
+            m_parser = nullptr;
+        }
+
+        ScopeRef& scope() { return m_scope; }
+
+    private:
+        ScopeRef m_scope;
+        Parser* m_parser;
+    };
+
     ScopeRef currentScope()
     {
         return ScopeRef(&m_scopeStack, m_scopeStack.size() - 1);
@@ -496,53 +645,80 @@ private:
         return currentScope();
     }
     
-    bool popScopeInternal(ScopeRef& scope, bool shouldTrackClosedVariables)
+    void popScopeInternal(ScopeRef& scope, bool shouldTrackClosedVariables)
     {
         ASSERT_UNUSED(scope, scope.index() == m_scopeStack.size() - 1);
         ASSERT(m_scopeStack.size() > 1);
-        bool result = m_scopeStack[m_scopeStack.size() - 2].collectFreeVariables(&m_scopeStack.last(), shouldTrackClosedVariables);
+        m_scopeStack[m_scopeStack.size() - 2].collectFreeVariables(&m_scopeStack.last(), shouldTrackClosedVariables);
+        if (!m_scopeStack.last().isFunctionBoundary() && m_scopeStack.last().needsFullActivation())
+            m_scopeStack[m_scopeStack.size() - 2].setNeedsFullActivation();
         m_scopeStack.removeLast();
-        return result;
     }
     
-    bool popScope(ScopeRef& scope, bool shouldTrackClosedVariables)
+    ALWAYS_INLINE void popScope(ScopeRef& scope, bool shouldTrackClosedVariables)
     {
-        return popScopeInternal(scope, shouldTrackClosedVariables);
+        popScopeInternal(scope, shouldTrackClosedVariables);
     }
     
-    bool popScope(AutoPopScopeRef& scope, bool shouldTrackClosedVariables)
+    ALWAYS_INLINE void popScope(AutoPopScopeRef& scope, bool shouldTrackClosedVariables)
     {
         scope.setPopped();
-        return popScopeInternal(scope, shouldTrackClosedVariables);
+        popScopeInternal(scope, shouldTrackClosedVariables);
+    }
+
+    ALWAYS_INLINE void popScope(AutoCleanupLexicalScope& cleanupScope, bool shouldTrackClosedVariables)
+    {
+        RELEASE_ASSERT(cleanupScope.isValid());
+        ScopeRef& scope = cleanupScope.scope();
+        cleanupScope.setPopped();
+        popScopeInternal(scope, shouldTrackClosedVariables);
     }
     
-    bool declareVariable(const Identifier* ident)
+    enum class DeclarationType { VarDeclaration, LexicalDeclaration };
+    bool declareVariable(const Identifier* ident, typename Parser::DeclarationType type = DeclarationType::VarDeclaration, bool isConstant = false)
     {
         unsigned i = m_scopeStack.size() - 1;
         ASSERT(i < m_scopeStack.size());
-        while (!m_scopeStack[i].allowsNewDecls()) {
+
+        if (type == DeclarationType::VarDeclaration) {
+            while (!m_scopeStack[i].allowsVarDeclarations()) {
+                i--;
+                ASSERT(i < m_scopeStack.size());
+            }
+
+            return m_scopeStack[i].declareVariable(ident, isConstant);
+        }
+
+        ASSERT(type == DeclarationType::LexicalDeclaration);
+
+        // Lexical variables declared at a top level scope that shadow arguments or vars are not allowed.
+        if (m_statementDepth == 1 && (hasDeclaredParameter(*ident) || hasDeclaredVariable(*ident)))
+            return false;
+
+        while (!m_scopeStack[i].allowsLexicalDeclarations()) {
             i--;
             ASSERT(i < m_scopeStack.size());
         }
-        return m_scopeStack[i].declareVariable(ident);
+
+        return m_scopeStack[i].declareLexicalVariable(ident);
     }
     
     NEVER_INLINE bool hasDeclaredVariable(const Identifier& ident)
     {
         unsigned i = m_scopeStack.size() - 1;
         ASSERT(i < m_scopeStack.size());
-        while (!m_scopeStack[i].allowsNewDecls()) {
+        while (!m_scopeStack[i].allowsVarDeclarations()) {
             i--;
             ASSERT(i < m_scopeStack.size());
         }
         return m_scopeStack[i].hasDeclaredVariable(ident);
     }
-    
+
     NEVER_INLINE bool hasDeclaredParameter(const Identifier& ident)
     {
         unsigned i = m_scopeStack.size() - 1;
         ASSERT(i < m_scopeStack.size());
-        while (!m_scopeStack[i].allowsNewDecls()) {
+        while (!m_scopeStack[i].allowsVarDeclarations()) {
             i--;
             ASSERT(i < m_scopeStack.size());
         }
@@ -554,7 +730,7 @@ private:
         if (!m_syntaxAlreadyValidated || strictMode())
             m_scopeStack.last().declareWrite(ident);
     }
-    
+
     ScopeStack m_scopeStack;
     
     const SourceProviderCacheItem* findCachedFunctionInfo(int openBracePos) 
@@ -565,8 +741,7 @@ private:
     Parser();
     String parseInner();
 
-    void didFinishParsing(SourceElements*, DeclarationStacks::VarStack&, 
-        DeclarationStacks::FunctionStack&, CodeFeatures, int, IdentifierSet&, const Vector<RefPtr<UniquedStringImpl>>&&);
+    void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&, VariableEnvironment&, CodeFeatures, int, const Vector<RefPtr<UniquedStringImpl>>&&);
 
     // Used to determine type of error to report.
     bool isFunctionBodyNode(ScopeNode*) { return false; }
@@ -753,7 +928,7 @@ private:
         return true;
     }
     void pushLabel(const Identifier* label, bool isLoop) { currentScope()->pushLabel(label, isLoop); }
-    void popLabel() { currentScope()->popLabel(); }
+    void popLabel(ScopeRef scope) { scope->popLabel(); }
     ScopeLabelInfo* getLabel(const Identifier* label)
     {
         ScopeRef current = currentScope();
@@ -766,6 +941,11 @@ private:
         return result;
     }
 
+    ALWAYS_INLINE bool isLETMaskedAsIDENT()
+    {
+        return match(LET) && !strictMode();
+    }
+
     template <class TreeBuilder> TreeSourceElements parseSourceElements(TreeBuilder&, SourceElementsMode, FunctionParseType);
     template <class TreeBuilder> TreeStatement parseStatementListItem(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength);
     template <class TreeBuilder> TreeStatement parseStatement(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength = 0);
@@ -773,7 +953,7 @@ private:
     template <class TreeBuilder> TreeStatement parseClassDeclaration(TreeBuilder&);
 #endif
     template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&);