WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
patch
c-backup.diff (text/plain), 345.70 KB, created by
Saam Barati
on 2015-07-09 17:54:35 PDT
(
hide
)
Description:
patch
Filename:
MIME Type:
Creator:
Saam Barati
Created:
2015-07-09 17:54:35 PDT
Size:
345.70 KB
patch
obsolete
>Index: Source/JavaScriptCore/CMakeLists.txt >=================================================================== >--- Source/JavaScriptCore/CMakeLists.txt (revision 186653) >+++ Source/JavaScriptCore/CMakeLists.txt (working copy) >@@ -383,6 +383,7 @@ set(JavaScriptCore_SOURCES > parser/SourceCode.cpp > parser/SourceProvider.cpp > parser/SourceProviderCache.cpp >+ parser/VariableEnvironment.cpp > > profiler/LegacyProfiler.cpp > profiler/Profile.cpp >Index: Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj (revision 186653) >+++ Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj (working copy) >@@ -658,6 +658,7 @@ > <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" /> >@@ -1435,6 +1436,7 @@ > <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: Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters (revision 186653) >+++ Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters (working copy) >@@ -459,6 +459,9 @@ > <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> >@@ -2466,6 +2469,9 @@ > <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: Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (revision 186653) >+++ Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (working copy) >@@ -1020,6 +1020,8 @@ > 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, ); }; }; >@@ -2762,6 +2764,8 @@ > 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>"; }; >@@ -4321,6 +4325,8 @@ > E49DC15112EF272200184A1F /* SourceProviderCache.h */, > E49DC14912EF261A00184A1F /* SourceProviderCacheItem.h */, > A7A7EE7711B98B8D0065A14F /* SyntaxChecker.h */, >+ 79EE0BFD1B4AFB85000385C9 /* VariableEnvironment.cpp */, >+ 79EE0BFE1B4AFB85000385C9 /* VariableEnvironment.h */, > ); > path = parser; > sourceTree = "<group>"; >@@ -5623,6 +5629,7 @@ > 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 */, >@@ -7045,6 +7052,7 @@ > 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: Source/JavaScriptCore/builtins/BuiltinExecutables.cpp >=================================================================== >--- Source/JavaScriptCore/builtins/BuiltinExecutables.cpp (revision 186653) >+++ Source/JavaScriptCore/builtins/BuiltinExecutables.cpp (working copy) >@@ -103,7 +103,8 @@ UnlinkedFunctionExecutable* BuiltinExecu > 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: Source/JavaScriptCore/bytecode/BytecodeList.json >=================================================================== >--- Source/JavaScriptCore/bytecode/BytecodeList.json (revision 186653) >+++ Source/JavaScriptCore/bytecode/BytecodeList.json (working copy) >@@ -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 }, >@@ -57,8 +56,6 @@ > { "name" : "op_is_object_or_null", "length" : 3 }, > { "name" : "op_is_function", "length" : 3 }, > { "name" : "op_in", "length" : 4 }, >- { "name" : "op_init_global_const_nop", "length" : 5 }, >- { "name" : "op_init_global_const", "length" : 5 }, > { "name" : "op_get_by_id", "length" : 9 }, > { "name" : "op_get_by_id_out_of_line", "length" : 9 }, > { "name" : "op_get_array_length", "length" : 9 }, >@@ -113,6 +110,8 @@ > { "name" : "op_push_with_scope", "length" : 3 }, > { "name" : "op_pop_scope", "length" : 2 }, > { "name" : "op_push_name_scope", "length" : 5 }, >+ { "name" : "op_push_lexical_environment", "length" : 5 }, >+ { "name" : "op_pop_lexical_environment", "length" : 2 }, > { "name" : "op_catch", "length" : 3 }, > { "name" : "op_throw", "length" : 2 }, > { "name" : "op_throw_static_error", "length" : 3 }, >Index: Source/JavaScriptCore/bytecode/BytecodeUseDef.h >=================================================================== >--- Source/JavaScriptCore/bytecode/BytecodeUseDef.h (revision 186653) >+++ Source/JavaScriptCore/bytecode/BytecodeUseDef.h (working copy) >@@ -58,6 +58,7 @@ void computeUsesForBytecodeOffset( > case op_to_this: > case op_check_tdz: > case op_pop_scope: >+ case op_pop_lexical_environment: > case op_profile_will_call: > case op_profile_did_call: > case op_profile_type: >@@ -113,15 +114,13 @@ 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: > case op_to_index_string: >- case op_init_global_const_nop: >- case op_init_global_const: > case op_push_name_scope: > case op_push_with_scope: >+ case op_push_lexical_environment: > case op_resolve_scope: > case op_get_from_scope: > case op_to_primitive: >@@ -240,8 +239,6 @@ void computeDefsForBytecodeOffset(CodeBl > OpcodeID opcodeID = interpreter->getOpcodeID(instruction->u.opcode); > switch (opcodeID) { > // These don't define anything. >- case op_init_global_const: >- case op_init_global_const_nop: > case op_put_to_scope: > case op_end: > case op_profile_will_call: >@@ -298,8 +295,10 @@ void computeDefsForBytecodeOffset(CodeBl > case op_enumerator_structure_pname: > case op_enumerator_generic_pname: > case op_pop_scope: >+ case op_pop_lexical_environment: > case op_push_name_scope: > case op_push_with_scope: >+ case op_push_lexical_environment: > case op_resolve_scope: > case op_strcat: > case op_to_primitive: >@@ -373,8 +372,7 @@ void computeDefsForBytecodeOffset(CodeBl > 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: Source/JavaScriptCore/bytecode/CodeBlock.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/CodeBlock.cpp (revision 186653) >+++ Source/JavaScriptCore/bytecode/CodeBlock.cpp (working copy) >@@ -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); >@@ -1059,23 +1052,6 @@ void CodeBlock::dumpBytecode( > printBinaryOp(out, exec, location, it, "in"); > break; > } >- case op_init_global_const_nop: { >- printLocationAndOp(out, exec, location, it, "init_global_const_nop"); >- it++; >- it++; >- it++; >- it++; >- break; >- } >- case op_init_global_const: { >- WriteBarrier<Unknown>* variablePointer = (++it)->u.variablePointer; >- int r0 = (++it)->u.operand; >- printLocationAndOp(out, exec, location, it, "init_global_const"); >- out.printf("g%d(%p), %s", m_globalObject->findVariableIndex(variablePointer).offset(), variablePointer, registerName(r0).data()); >- it++; >- it++; >- break; >- } > case op_get_by_id: > case op_get_by_id_out_of_line: > case op_get_array_length: { >@@ -1473,6 +1449,11 @@ void CodeBlock::dumpBytecode( > printLocationOpAndRegisterOperand(out, exec, location, it, "pop_scope", r0); > break; > } >+ case op_pop_lexical_environment: { >+ int r0 = (++it)->u.operand; >+ printLocationOpAndRegisterOperand(out, exec, location, it, "pop_lexical_environment", r0); >+ break; >+ } > case op_push_name_scope: { > int dst = (++it)->u.operand; > int r1 = (++it)->u.operand; >@@ -1482,6 +1463,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_push_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, "push_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; >@@ -1875,6 +1866,10 @@ CodeBlock::CodeBlock(ScriptExecutable* o > UnlinkedInstructionStream::Reader instructionReader(unlinkedCodeBlock->instructions()); > > Vector<Instruction, 0, UnsafeVectorOverflow> instructions(instructionCount); >+ >+ Vector<SymbolTable*> symbolTableStack; >+ HashSet<int, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> clonedConstantSymbolTables; >+ > for (unsigned i = 0; !instructionReader.atEnd(); ) { > const UnlinkedInstruction* pc = instructionReader.next(); > >@@ -1967,15 +1962,38 @@ CodeBlock::CodeBlock(ScriptExecutable* o > case op_get_array_length: > CRASH(); > >- case op_init_global_const_nop: { >- ASSERT(codeType() == GlobalCode); >- Identifier ident = identifier(pc[4].u.operand); >- SymbolTableEntry entry = m_globalObject->symbolTable()->get(ident.impl()); >- if (entry.isNull()) >- break; >+ case op_push_lexical_environment: { >+ int symbolTableIndex = pc[3].u.operand; >+ SymbolTable* unlinkedTable = jsCast<SymbolTable*>(getConstant(symbolTableIndex)); >+ // Different push_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 >+ // create two push_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. We can cache our results >+ // so we don't have extraneous calls to SymbolTable::cloneScopePart(). >+ SymbolTable* linkedTable; >+ auto addResult = clonedConstantSymbolTables.add(symbolTableIndex); >+ if (addResult.isNewEntry) { >+ 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. >+ linkedTable = symbolTable(); >+ if (linkedTable->scopeSize()) >+ RELEASE_ASSERT(didCloneSymbolTable); >+ } >+ replaceConstant(symbolTableIndex, linkedTable); >+ } else >+ linkedTable = unlinkedTable; >+ >+ symbolTableStack.append(linkedTable); >+ break; >+ } > >- instructions[i + 0] = vm()->interpreter->getOpcode(op_init_global_const); >- instructions[i + 1] = &m_globalObject->variableAt(entry.varOffset().scopeOffset()); >+ case op_pop_lexical_environment: { >+ symbolTableStack.removeLast(); > break; > } > >@@ -1984,7 +2002,7 @@ CodeBlock::CodeBlock(ScriptExecutable* o > ResolveType type = static_cast<ResolveType>(pc[4].u.operand); > RELEASE_ASSERT(type != LocalClosureVar); > >- ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), needsActivation(), scope, ident, Get, type); >+ ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), symbolTableStack.size(), scope, ident, Get, type); > instructions[i + 4].u.operand = op.type; > instructions[i + 5].u.operand = op.depth; > if (op.lexicalEnvironment) >@@ -2008,7 +2026,7 @@ CodeBlock::CodeBlock(ScriptExecutable* o > > 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(), symbolTableStack.size(), scope, ident, Get, modeAndType.type()); > > instructions[i + 4].u.operand = ResolveModeAndType(modeAndType.mode(), op.type).operand(); > if (op.type == GlobalVar || op.type == GlobalVarWithVarInjectionChecks) >@@ -2025,13 +2043,20 @@ CodeBlock::CodeBlock(ScriptExecutable* o > 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); > 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)); >- iter->value.prepareToWatch(); >- instructions[i + 5].u.watchpointSet = iter->value.watchpointSet(); >+ bool found = false; >+ for (unsigned stackIdx = symbolTableStack.size(); stackIdx--; ) { >+ SymbolTable* symbolTable = symbolTableStack[stackIdx]; >+ ConcurrentJITLocker locker(symbolTable->m_lock); >+ SymbolTable::Map::iterator iter = symbolTable->find(locker, ident.impl()); >+ if (iter != symbolTable->end(locker)) { >+ iter->value.prepareToWatch(); >+ instructions[i + 5].u.watchpointSet = iter->value.watchpointSet(); >+ found = true; >+ break; >+ } >+ } >+ RELEASE_ASSERT(found); > } else > instructions[i + 5].u.watchpointSet = nullptr; > break; >@@ -2039,7 +2064,7 @@ CodeBlock::CodeBlock(ScriptExecutable* o > > const Identifier& ident = identifier(pc[2].u.operand); > >- ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), needsActivation(), scope, ident, Put, modeAndType.type()); >+ ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), symbolTableStack.size(), scope, ident, Put, modeAndType.type()); > > instructions[i + 4].u.operand = ResolveModeAndType(modeAndType.mode(), op.type).operand(); > if (op.type == GlobalVar || op.type == GlobalVarWithVarInjectionChecks) >@@ -2071,7 +2096,7 @@ CodeBlock::CodeBlock(ScriptExecutable* o > 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(), symbolTableStack.size(), 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 >Index: Source/JavaScriptCore/bytecode/CodeBlock.h >=================================================================== >--- Source/JavaScriptCore/bytecode/CodeBlock.h (revision 186653) >+++ Source/JavaScriptCore/bytecode/CodeBlock.h (working copy) >@@ -937,6 +937,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()); >Index: Source/JavaScriptCore/bytecode/EvalCodeCache.h >=================================================================== >--- Source/JavaScriptCore/bytecode/EvalCodeCache.h (revision 186653) >+++ Source/JavaScriptCore/bytecode/EvalCodeCache.h (working copy) >@@ -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: Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp (revision 186653) >+++ Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp (working copy) >@@ -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* genera > > 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::UnlinkedFunc > , m_functionMode(node->functionMode()) > { > ASSERT(m_constructorKind == static_cast<unsigned>(node->constructorKind())); >+ m_parentScopeTDZVariables.swap(parentScopeTDZVariables); > } > > size_t UnlinkedFunctionExecutable::parameterCount() const >Index: Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h >=================================================================== >--- Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h (revision 186653) >+++ Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h (working copy) >@@ -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; >@@ -645,14 +648,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 +659,7 @@ private: > { > } > >- VariableDeclations m_varDeclarations; >+ VariableEnvironment m_varDeclarations; > > public: > static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) >Index: Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >=================================================================== >--- Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp (revision 186653) >+++ Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp (working copy) >@@ -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& > , m_codeType(GlobalCode) > , m_vm(&vm) > { >+ ASSERT_UNUSED(parentScopeTDZVariables, !parentScopeTDZVariables->size()); >+ > for (auto& constantRegister : m_linkTimeConstantRegisters) > constantRegister = nullptr; > >@@ -166,20 +183,20 @@ BytecodeGenerator::BytecodeGenerator(VM& > > 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()) >+ RELEASE_ASSERT(entry.value.isVar()); >+ } >+ 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 +226,10 @@ BytecodeGenerator::BytecodeGenerator(VM& > 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()) { >@@ -248,9 +265,12 @@ BytecodeGenerator::BytecodeGenerator(VM& > if (shouldCaptureSomeOfTheThings) { > m_lexicalEnvironmentRegister = addVar(); > m_codeBlock->setActivationRegister(m_lexicalEnvironmentRegister->virtualRegister()); >- emitOpcode(op_create_lexical_environment); >+ emitOpcode(op_push_lexical_environment); > instructions().append(m_lexicalEnvironmentRegister->index()); > instructions().append(scopeRegister()->index()); >+ instructions().append(addConstantValue(m_symbolTable->cloneScopePart(*m_vm))->index()); >+ instructions().append(addConstantValue(jsUndefined())->index()); >+ > emitOpcode(op_mov); > instructions().append(scopeRegister()->index()); > instructions().append(m_lexicalEnvironmentRegister->index()); >@@ -393,12 +413,14 @@ BytecodeGenerator::BytecodeGenerator(VM& > 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: >@@ -495,9 +517,12 @@ BytecodeGenerator::BytecodeGenerator(VM& > instructions().append(0); > instructions().append(0); > } >+ >+ m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(*m_vm, m_symbolTable), m_lexicalEnvironmentRegister, false }); >+ 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()) >@@ -521,15 +546,18 @@ BytecodeGenerator::BytecodeGenerator(VM& > 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()); >+ 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 +592,23 @@ RegisterID* BytecodeGenerator::newRegist > 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; >@@ -1219,28 +1258,233 @@ RegisterID* BytecodeGenerator::emitLoadG > return m_globalObjectRegister; > } > >-Variable BytecodeGenerator::variable(const Identifier& property) >+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.isConstant()); >+ ASSERT(!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()); >+ } >+ >+ SymbolTableEntry newEntry(varOffset, entry.value.isConstant() ? ReadOnly : 0); >+ symbolTable->add(locker, entry.key.get(), newEntry); >+ } >+ } >+ >+ RegisterID* newScope = nullptr; >+ if (hasCapturedVariables) { >+ newScope = newBlockScopeVariable(); >+ newScope->ref(); >+ >+ RegisterID* constantSymbolTable = addConstantValue(symbolTable->cloneScopePart(*m_vm)); >+ if (constantSymbolTableResult) >+ *constantSymbolTableResult = constantSymbolTable; >+ >+ emitOpcode(op_push_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 }); >+ 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(®isterFor(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 = ®isterFor(offset.stackOffset()); >+ local->deref(); >+ } >+ >+ if (hasCapturedVariables) { >+ RELEASE_ASSERT(stackEntry.m_scope); >+ stackEntry.m_scope->deref(); >+ emitOpcode(op_pop_lexical_environment); >+ instructions().append(scopeRegister()->index()); >+ } >+ >+ 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), 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. >+ emitOpcode(op_pop_lexical_environment); >+ instructions().append(scopeRegister()->index()); >+ >+ emitOpcode(op_push_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), transitionValue, DoNotThrowIfNotFound); >+ transitionValue->deref(); >+ } >+ } >+} >+ >+std::pair<Variable, bool> BytecodeGenerator::resolveVariable(const Identifier& property) > { > if (property == propertyNames().thisIdentifier) { >- return Variable( >+ return std::make_pair(Variable( > property, VarOffset(thisRegister()->virtualRegister()), thisRegister(), >- ReadOnly, Variable::SpecialVariable); >+ ReadOnly, Variable::SpecialVariable), false); > } > >- 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 std::make_pair(Variable(property), false); >+ Strong<SymbolTable>& symbolTable = stackEntry.m_symbolTable; >+ SymbolTableEntry symbolTableEntry = symbolTable->get(property.impl()); >+ if (symbolTableEntry.isNull()) >+ continue; >+ >+ return std::make_pair(variableForLocalEntry(property, symbolTableEntry), symbolTable->correspondsToLexicalScope()); > } >- >- return variableForLocalEntry(property, entry); >+ >+ return std::make_pair(Variable(property), false); >+} >+ >+Variable BytecodeGenerator::variable(const Identifier& property) >+{ >+ return resolveVariable(property).first; > } > > Variable BytecodeGenerator::variablePerSymbolTable(const Identifier& property) >@@ -1271,7 +1515,6 @@ void BytecodeGenerator::createVariable( > ExistingVariableMode existingVariableMode) > { > ASSERT(property != propertyNames().thisIdentifier); >- > ConcurrentJITLocker locker(symbolTable().m_lock); > SymbolTableEntry entry = symbolTable().get(locker, property.impl()); > >@@ -1348,7 +1591,22 @@ RegisterID* BytecodeGenerator::emitResol > // 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. >+ RELEASE_ASSERT(!stackEntry.m_isWithOrCatch); >+ >+ 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. >@@ -1448,26 +1706,8 @@ RegisterID* BytecodeGenerator::emitPutTo > > 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); > } > >@@ -1480,17 +1720,6 @@ RegisterID* BytecodeGenerator::emitInsta > return dst; > } > >-RegisterID* BytecodeGenerator::emitInitGlobalConst(const Identifier& identifier, RegisterID* value) >-{ >- ASSERT(m_codeType == GlobalCode); >- emitOpcode(op_init_global_const_nop); >- instructions().append(0); >- instructions().append(value->index()); >- instructions().append(0); >- instructions().append(addConstant(identifier)); >- return value; >-} >- > RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property) > { > m_codeBlock->addPropertyAccessInstruction(instructions().size()); >@@ -1691,6 +1920,55 @@ void BytecodeGenerator::emitTDZCheck(Reg > 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 +2457,9 @@ RegisterID* BytecodeGenerator::emitPushW > 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 }); >+ return result; > } > > void BytecodeGenerator::emitPopScope(RegisterID* srcDst) >@@ -2192,6 +2472,8 @@ void BytecodeGenerator::emitPopScope(Reg > > 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 +2509,7 @@ void BytecodeGenerator::pushFinallyConte > 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 +2535,7 @@ void BytecodeGenerator::pushIteratorClos > 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 > }; >@@ -2399,6 +2683,7 @@ void BytecodeGenerator::emitComplexPopSc > 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 +2696,7 @@ void BytecodeGenerator::emitComplexPopSc > 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 +2731,10 @@ void BytecodeGenerator::emitComplexPopSc > 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 +2772,8 @@ void BytecodeGenerator::emitComplexPopSc > } > if (flipLabelScopes) > m_labelScopes = savedLabelScopes; >+ if (flipSymbolTableStack) >+ m_symbolTableStack = savedSymbolTableStack; > m_finallyDepth = savedFinallyDepth; > m_localScopeDepth = savedDynamicScopeDepth; > >@@ -2542,7 +2834,7 @@ void BytecodeGenerator::popTryAndEmitCat > 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 +2842,27 @@ void BytecodeGenerator::popTryAndEmitCat > 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; >+} >+ > void BytecodeGenerator::emitThrowReferenceError(const String& message) > { > emitOpcode(op_throw_static_error); >@@ -2585,6 +2898,8 @@ void BytecodeGenerator::emitPushCatchSco > 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 }); > } > > void BytecodeGenerator::beginSwitch(RegisterID* scrutineeRegister, SwitchInfo::SwitchType type) >@@ -2717,16 +3032,26 @@ bool BytecodeGenerator::isArgumentNumber > return registerID->index() == CallFrame::argumentOffset(argumentNumber); > } > >-void BytecodeGenerator::emitReadOnlyExceptionIfNeeded() >+bool BytecodeGenerator::isConstVariable(const Variable& variable) > { >- if (!isStrictMode()) >- return; >+ std::pair<Variable, bool> variablePair = resolveVariable(variable.ident()); >+ bool isLexicalScope = variablePair.second; >+ if (!isLexicalScope) >+ return false; >+ return variablePair.first.isReadOnly(); >+} >+ >+bool BytecodeGenerator::emitReadOnlyExceptionIfNeeded(const Variable& variable) >+{ >+ if (!isStrictMode() && !isConstVariable(variable)) >+ return false; > emitOpcode(op_throw_static_error); > instructions().append(addConstantValue(addStringConstant(Identifier::fromString(m_vm, StrictModeReadonlyPropertyWriteError)))->index()); > instructions().append(false); >+ return true; > } > >-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 +3113,9 @@ void BytecodeGenerator::emitEnumeration( > } > > emitLabel(scope->continueTarget()); >+ if (forLoopNode) >+ prepareLexicalScopeForNextForLoopIteration(forLoopNode, forLoopSymbolTable); >+ > { > emitIteratorNext(value.get(), iterator.get(), node); > emitJumpIfTrue(emitGetById(newTemporary(), value.get(), propertyNames().done), loopDone.get()); >Index: Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >=================================================================== >--- Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h (revision 186653) >+++ Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h (working copy) >@@ -91,6 +91,7 @@ namespace JSC { > unsigned forInContextStackSize; > unsigned tryContextStackSize; > unsigned labelScopesSize; >+ unsigned symbolTableStackSize; > int finallyDepth; > int dynamicScopeDepth; > }; >@@ -255,12 +256,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 +310,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 +457,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 > >@@ -473,8 +483,6 @@ namespace JSC { > RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_typeof, dst, src); } > RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base) { return emitBinaryOp(op_in, dst, property, base, OperandTypes()); } > >- RegisterID* emitInitGlobalConst(const Identifier&, RegisterID* value); >- > RegisterID* emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property); > RegisterID* emitPutById(RegisterID* base, const Identifier& property, RegisterID* value); > RegisterID* emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value, PropertyNode::PutType); >@@ -503,7 +511,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*); >@@ -549,7 +557,8 @@ namespace JSC { > RegisterID* emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node); > void emitIteratorClose(RegisterID* iterator, const ThrowableExpressionData* node); > >- void emitReadOnlyExceptionIfNeeded(); >+ bool emitReadOnlyExceptionIfNeeded(const Variable&); >+ bool isConstVariable(const Variable&); > > // Start a try block. 'start' must have been emitted. > TryData* pushTry(Label* start); >@@ -605,7 +614,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: >+ std::pair<Variable, bool> resolveVariable(const Identifier&); >+ void reclaimFreeRegisters(); > Variable variableForLocalEntry(const Identifier&, const SymbolTableEntry&); > > void emitOpcode(OpcodeID); >@@ -677,9 +692,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 +710,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 +719,13 @@ namespace JSC { > bool m_shouldEmitProfileHooks; > > SymbolTable* m_symbolTable { nullptr }; >+ struct SymbolTableStackEntry { >+ Strong<SymbolTable> m_symbolTable; >+ RegisterID* m_scope; >+ bool m_isWithOrCatch; >+ }; >+ Vector<SymbolTableStackEntry> m_symbolTableStack; >+ Vector<std::pair<VariableEnvironment, bool>> m_TDZStack; > > ScopeNode* const m_scopeNode; > Strong<UnlinkedCodeBlock> m_codeBlock; >@@ -752,6 +753,8 @@ namespace JSC { > int m_localScopeDepth { 0 }; > const CodeType m_codeType; > >+ int calculateTargetScopeDepthForExceptionHandler() const; >+ > Vector<ControlFlowContext, 0, UnsafeVectorOverflow> m_scopeContextStack; > Vector<SwitchInfo> m_switchContextStack; > Vector<std::unique_ptr<ForInContext>> m_forInContextStack; >Index: Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >=================================================================== >--- Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp (revision 186653) >+++ Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp (working copy) >@@ -196,6 +196,7 @@ RegisterID* ResolveNode::emitBytecode(By > { > 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(By > 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::emi > > 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::emi > 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,9 +1062,10 @@ RegisterID* PostfixNode::emitResolve(Byt > > Variable var = generator.variable(ident); > if (RegisterID* local = var.local()) { >+ generator.emitTDZCheckIfNecessary(var, local, nullptr); > RefPtr<RegisterID> localReg = local; > if (var.isReadOnly()) { >- generator.emitReadOnlyExceptionIfNeeded(); >+ generator.emitReadOnlyExceptionIfNeeded(var); > localReg = generator.emitMove(generator.tempDestination(dst), local); > } else if (generator.vm()->typeProfiler()) { > RefPtr<RegisterID> tempDst = generator.finalDestination(dst); >@@ -1080,6 +1085,12 @@ RegisterID* PostfixNode::emitResolve(Byt > 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); >+ if (var.isReadOnly()) { >+ bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); >+ if (threwException) >+ return value.get(); >+ } > RefPtr<RegisterID> oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator); > generator.emitPutToScope(scope.get(), var, value.get(), ThrowIfNotFound); > if (generator.vm()->typeProfiler()) { >@@ -1159,12 +1170,16 @@ RegisterID* PostfixNode::emitBytecode(By > > RegisterID* DeleteResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) > { >+ // FIXME: When supporting lexical variables outside strict mode, this should emit TDZ checks. > 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 +1236,7 @@ RegisterID* TypeOfResolveNode::emitBytec > { > 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 +1244,7 @@ RegisterID* TypeOfResolveNode::emitBytec > > 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,9 +1272,10 @@ RegisterID* PrefixNode::emitResolve(Byte > > Variable var = generator.variable(ident); > if (RegisterID* local = var.local()) { >+ generator.emitTDZCheckIfNecessary(var, local, nullptr); > RefPtr<RegisterID> localReg = local; > if (var.isReadOnly()) { >- generator.emitReadOnlyExceptionIfNeeded(); >+ generator.emitReadOnlyExceptionIfNeeded(var); > localReg = generator.emitMove(generator.tempDestination(dst), localReg.get()); > } else if (generator.vm()->typeProfiler()) { > RefPtr<RegisterID> tempDst = generator.tempDestination(dst); >@@ -1275,6 +1293,13 @@ RegisterID* PrefixNode::emitResolve(Byte > 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); >+ if (var.isReadOnly()) { >+ bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); >+ if (threwException) >+ return value.get(); >+ } >+ > emitIncOrDec(generator, value.get(), m_operator); > generator.emitPutToScope(scope.get(), var, value.get(), ThrowIfNotFound); > if (generator.vm()->typeProfiler()) { >@@ -1772,8 +1797,9 @@ RegisterID* ReadModifyResolveNode::emitB > 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(); >+ generator.emitReadOnlyExceptionIfNeeded(var); > return emitReadModifyAssignment(generator, generator.finalDestination(dst), local, m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); > } > >@@ -1797,6 +1823,12 @@ RegisterID* ReadModifyResolveNode::emitB > 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); >+ if (var.isReadOnly()) { >+ bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); >+ if (threwException) >+ return generator.emitNode(dst, m_right); >+ } > 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,36 +1844,53 @@ RegisterID* AssignResolveNode::emitBytec > { > 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()) { >+ generator.emitReadOnlyExceptionIfNeeded(var); >+ 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); >+ if (var.isReadOnly()) { >+ bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); >+ if (threwException) >+ return result.get(); >+ } > generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); > RegisterID* returnResult = generator.emitPutToScope(scope.get(), var, result.get(), generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound); > 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; > } > >@@ -1949,43 +1998,28 @@ RegisterID* ConstDeclNode::emitCodeSingl > { > // FIXME: This code does not match the behavior of const in Firefox. > Variable var = generator.variable(m_ident); >+ // We should always be able to resolve a 'const' node because it will either be a stack allocated variable >+ // or on topmost lexical environment. >+ RELEASE_ASSERT(var.offset().kind() == VarKind::Stack || var.offset().kind() == VarKind::Scope); >+ RELEASE_ASSERT(var.isReadOnly()); > if (RegisterID* local = var.local()) { >- if (!m_init) >- return local; >- >- // FIXME: Maybe call emitExpressionInfo here. >- if (var.isSpecial() || generator.vm()->typeProfiler()) { >- RefPtr<RegisterID> tempDst = generator.newTemporary(); >- generator.emitNode(tempDst.get(), m_init); >- return generator.emitMove(local, tempDst.get()); >- } >- >- return generator.emitNode(local, m_init); >- } >- >- RefPtr<RegisterID> value = m_init ? generator.emitNode(m_init) : generator.emitLoad(0, jsUndefined()); >- >- if (generator.codeType() == GlobalCode) >- return generator.emitInitGlobalConst(m_ident, value.get()); >- >- if (generator.codeType() != EvalCode) { >- // Do a special kind of resolution. If anything fails, then don't perform the assignment. This is >- // pretty shady - particularly how negligent it is with inteleaving scopes - but it's the >- // behavior that JSC has had for a long time. >+ >+ //if (var.isSpecial() || generator.vm()->typeProfiler()) { >+ // RefPtr<RegisterID> tempDst = generator.newTemporary(); >+ // generator.emitNode(tempDst.get(), m_init); >+ // return generator.emitMove(local, tempDst.get()); >+ //} > >- ASSERT(generator.codeType() == FunctionCode); >- >- var = generator.variablePerSymbolTable(m_ident); >- if (!var.isResolved()) >- return value.get(); >- >- RefPtr<RegisterID> scope = generator.emitResolveScope(generator.newTemporary(), var); >- return generator.emitPutToScope(scope.get(), var, value.get(), DoNotThrowIfNotFound); >+ RegisterID* result = generator.emitNode(local, m_init); >+ generator.liftTDZCheckIfPossible(var); >+ return result; > } > >- // FIXME: This will result in incorrect assignment if m_ident exists in an intervening with scope. >+ RefPtr<RegisterID> value = generator.emitNode(m_init); > RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); >- return generator.emitPutToScope(scope.get(), var, value.get(), DoNotThrowIfNotFound); >+ RegisterID* result = generator.emitPutToScope(scope.get(), var, value.get(), DoNotThrowIfNotFound); >+ generator.liftTDZCheckIfPossible(var); >+ return result; > } > > RegisterID* ConstDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) >@@ -2035,7 +2069,9 @@ void BlockNode::emitBytecode(BytecodeGen > { > if (!m_statements) > return; >+ generator.pushLexicalScope(this, true); > m_statements->emitBytecode(generator, dst); >+ generator.popLexicalScope(this); > } > > // ------------------------------ EmptyStatementNode --------------------------- >@@ -2061,9 +2097,9 @@ void ExprStatementNode::emitBytecode(Byt > 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 +2110,7 @@ void VarStatementNode::emitBytecode(Byte > > 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 +2125,31 @@ RegisterID* EmptyVarExpression::emitByte > > 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 +2279,9 @@ void ForNode::emitBytecode(BytecodeGener > { > 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 +2299,7 @@ void ForNode::emitBytecode(BytecodeGener > > 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 +2309,7 @@ void ForNode::emitBytecode(BytecodeGener > generator.emitJump(topOfLoop.get()); > > generator.emitLabel(scope->breakTarget()); >+ generator.popLexicalScope(this); > generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0)); > } > >@@ -2351,6 +2417,9 @@ void ForInNode::emitMultiLoopBytecode(By > > 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 +2464,7 @@ void ForInNode::emitMultiLoopBytecode(By > generator.emitProfileControlFlow(profilerEndOffset); > > generator.emitLabel(scope->continueTarget()); >+ generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable); > generator.emitInc(i.get()); > generator.emitJump(loopStart.get()); > >@@ -2432,6 +2502,7 @@ void ForInNode::emitMultiLoopBytecode(By > 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 +2538,7 @@ void ForInNode::emitMultiLoopBytecode(By > 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 +2550,7 @@ void ForInNode::emitMultiLoopBytecode(By > > generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset()); > generator.emitLabel(end.get()); >+ generator.popLexicalScope(this); > generator.emitProfileControlFlow(profilerEndOffset); > } > >@@ -2494,6 +2567,8 @@ void ForOfNode::emitBytecode(BytecodeGen > 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 +2618,8 @@ void ForOfNode::emitBytecode(BytecodeGen > 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 +2894,10 @@ void SwitchNode::emitBytecode(BytecodeGe > 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()); >@@ -3319,25 +3398,34 @@ void ObjectPatternNode::collectBoundIden > void BindingNode::bindValue(BytecodeGenerator& generator, RegisterID* value) const > { > Variable var = generator.variable(m_boundProperty); >- if (RegisterID* local = var.local()) { >- if (var.isReadOnly()) { >- generator.emitReadOnlyExceptionIfNeeded(); >+ if (var.isReadOnly() && m_bindingContext == AssignmentContext::AssignmentExpression) { >+ bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); >+ if (threwException) > return; >- } >+ } >+ if (RegisterID* local = var.local()) { >+ if (m_bindingContext == AssignmentContext::AssignmentExpression) >+ generator.emitTDZCheckIfNecessary(var, local, nullptr); > 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: Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp >=================================================================== >--- Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp (revision 186653) >+++ Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp (working copy) >@@ -192,7 +192,11 @@ JSValue DebuggerCallFrame::evaluate(cons > 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: Source/JavaScriptCore/debugger/DebuggerScope.cpp >=================================================================== >--- Source/JavaScriptCore/debugger/DebuggerScope.cpp (revision 186653) >+++ Source/JavaScriptCore/debugger/DebuggerScope.cpp (working copy) >@@ -26,10 +26,12 @@ > #include "config.h" > #include "DebuggerScope.h" > >+#include "Exception.h" > #include "JSLexicalEnvironment.h" > #include "JSCInlines.h" > #include "JSNameScope.h" > #include "JSWithScope.h" >+#include "NativeErrorConstructor.h" > > namespace JSC { > >@@ -86,7 +88,16 @@ bool DebuggerScope::getOwnPropertySlot(J > // 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()) { >+ // We hit a scope property that has the TDZ empty value. >+ // Currently, returning false here causes the inspector to show this >+ // variable as being 'undefined'. This is not ideal and we should fix it. >+ // FIXME: 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: Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (revision 186653) >+++ Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (working copy) >@@ -214,6 +214,7 @@ private: > Node* base, unsigned identifierNumber, Node* value, const PutByIdStatus&, > bool isDirect); > void emitChecks(const ConstantStructureCheckVector&); >+ void handleTDZCheck(VirtualRegister); > > void prepareToParseBlock(); > void clearCaches(); >@@ -2570,6 +2571,11 @@ void ByteCodeParser::handlePutById( > } } > } > >+void ByteCodeParser::handleTDZCheck(VirtualRegister operand) >+{ >+ addToGraph(CheckNotEmpty, get(operand)); >+} >+ > void ByteCodeParser::prepareToParseBlock() > { > clearCaches(); >@@ -2903,8 +2909,7 @@ bool ByteCodeParser::parseBlock(unsigned > } > > case op_check_tdz: { >- Node* op = get(VirtualRegister(currentInstruction[1].u.operand)); >- addToGraph(CheckNotEmpty, op); >+ handleTDZCheck(VirtualRegister(currentInstruction[1].u.operand)); > NEXT_OPCODE(op_check_tdz); > } > >@@ -3153,20 +3158,6 @@ bool ByteCodeParser::parseBlock(unsigned > NEXT_OPCODE(op_put_by_id); > } > >- case op_init_global_const_nop: { >- NEXT_OPCODE(op_init_global_const_nop); >- } >- >- case op_init_global_const: { >- Node* value = get(VirtualRegister(currentInstruction[2].u.operand)); >- JSGlobalObject* globalObject = m_inlineStackTop->m_codeBlock->globalObject(); >- addToGraph( >- PutGlobalVar, >- OpInfo(globalObject->assertVariableIsInThisObject(currentInstruction[1].u.variablePointer)), >- weakJSConstant(globalObject), value); >- NEXT_OPCODE(op_init_global_const); >- } >- > case op_profile_type: { > Node* valueToProfile = get(VirtualRegister(currentInstruction[1].u.operand)); > addToGraph(ProfileType, OpInfo(currentInstruction[2].u.location), valueToProfile); >@@ -3727,14 +3718,27 @@ bool ByteCodeParser::parseBlock(unsigned > NEXT_OPCODE(op_loop_hint); > } > >- 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))); >+ case op_push_lexical_environment: { >+ 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); >+ addToGraph(Phantom, scope); >+ NEXT_OPCODE(op_push_lexical_environment); > } >- >+ >+ case op_pop_lexical_environment: { >+ Node* currentScope = get(VirtualRegister(currentInstruction[1].u.operand)); >+ Node* newScope = addToGraph(SkipScope, currentScope); >+ set(VirtualRegister(currentInstruction[1].u.operand), newScope); >+ addToGraph(Phantom, currentScope); >+ NEXT_OPCODE(op_pop_lexical_environment); >+ } >+ > 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: Source/JavaScriptCore/dfg/DFGCapabilities.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGCapabilities.cpp (revision 186653) >+++ Source/JavaScriptCore/dfg/DFGCapabilities.cpp (working copy) >@@ -152,8 +152,6 @@ CapabilityLevel capabilityLevel(OpcodeID > case op_put_by_id_transition_direct_out_of_line: > case op_put_by_id_transition_normal: > case op_put_by_id_transition_normal_out_of_line: >- case op_init_global_const_nop: >- case op_init_global_const: > case op_jmp: > case op_jtrue: > case op_jfalse: >@@ -207,7 +205,8 @@ CapabilityLevel capabilityLevel(OpcodeID > case op_to_index_string: > case op_new_func: > case op_new_func_exp: >- case op_create_lexical_environment: >+ case op_push_lexical_environment: >+ case op_pop_lexical_environment: > return CanCompileAndInline; > > case op_put_to_scope: { >Index: Source/JavaScriptCore/dfg/DFGNode.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNode.h (revision 186653) >+++ Source/JavaScriptCore/dfg/DFGNode.h (working copy) >@@ -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: Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp (revision 186653) >+++ Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp (working copy) >@@ -595,15 +595,15 @@ private: > > { > SymbolTable* symbolTable = node->castOperand<SymbolTable*>(); >- Node* undefined = m_insertionSet.insertConstant( >- nodeIndex + 1, node->origin, jsUndefined()); >+ Node* initialValue = m_insertionSet.insertConstant( >+ nodeIndex + 1, node->origin, node->initializationValueForActivation()); > ConcurrentJITLocker locker(symbolTable->m_lock); > for (auto iter = symbolTable->begin(locker), end = symbolTable->end(locker); iter != end; ++iter) { > m_insertionSet.insert( > nodeIndex + 1, > PromotedHeapLocation( > ClosureVarPLoc, node, iter->value.scopeOffset().offset()).createHint( >- m_graph, node->origin, undefined)); >+ m_graph, node->origin, initialValue)); > } > } > >Index: Source/JavaScriptCore/dfg/DFGOperations.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGOperations.cpp (revision 186653) >+++ Source/JavaScriptCore/dfg/DFGOperations.cpp (working copy) >@@ -762,11 +762,13 @@ char* JIT_OPERATION operationNewFloat64A > return newTypedArrayWithOneArgument<JSFloat64Array>(exec, structure, encodedValue); > } > >-JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState* exec, Structure* structure, JSScope* scope, SymbolTable* table) >+JSCell* JIT_OPERATION operationPushLexicalEnvironmentDirect(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: Source/JavaScriptCore/dfg/DFGOperations.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGOperations.h (revision 186653) >+++ Source/JavaScriptCore/dfg/DFGOperations.h (working copy) >@@ -96,7 +96,7 @@ EncodedJSValue JIT_OPERATION operationRe > 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 operationPushLexicalEnvironmentDirect(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: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (revision 186653) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (working copy) >@@ -4690,6 +4690,8 @@ void SpeculativeJIT::compileCreateActiva > > 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); >@@ -4697,7 +4699,13 @@ void SpeculativeJIT::compileCreateActiva > > flushRegisters(); > >- callOperation(operationCreateActivationDirect, resultGPR, structure, scopeGPR, table); >+#if USE(JSVALUE64) >+ callOperation(operationPushLexicalEnvironmentDirect, >+ resultGPR, structure, scopeGPR, table, TrustedImm64(JSValue::encode(initializationValue))); >+#else >+ callOperation(operationPushLexicalEnvironmentDirect, >+ resultGPR, structure, scopeGPR, table, TrustedImm32(initializationValue.tag()), TrustedImm32(initializationValue.payload())); >+#endif > cellResult(resultGPR, node); > return; > } >@@ -4721,17 +4729,23 @@ void SpeculativeJIT::compileCreateActiva > 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, operationPushLexicalEnvironmentDirect, resultGPR, structure, scopeGPR, table, TrustedImm64(JSValue::encode(initializationValue)))); >+#else >+ addSlowPathGenerator( >+ slowPathCall( >+ slowPath, this, operationPushLexicalEnvironmentDirect, resultGPR, structure, scopeGPR, table, TrustedImm32(initializationValue.tag()), TrustedImm32(initializationValue.payload()))); >+#endif > > cellResult(resultGPR, node); > } >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (revision 186653) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (working copy) >@@ -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: Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h >=================================================================== >--- Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h (revision 186653) >+++ Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h (working copy) >@@ -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: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (revision 186653) >+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (working copy) >@@ -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)); >+ m_out.operation(operationPushLexicalEnvironmentDirect), m_callFrame, weakPointer(structure), >+ 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]); > } > >@@ -3089,8 +3090,8 @@ private: > > m_out.appendTo(slowPath, continuation); > LValue callResult = vmCall( >- m_out.operation(operationCreateActivationDirect), m_callFrame, weakPointer(structure), >- scope, weakPointer(table)); >+ m_out.operation(operationPushLexicalEnvironmentDirect), m_callFrame, weakPointer(structure), >+ scope, weakPointer(table), m_out.constInt64(JSValue::encode(initializationValue))); > ValueFromBlock slowResult = m_out.anchor(callResult); > m_out.jump(continuation); > >@@ -5413,9 +5414,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)); >+ m_out.operation(operationPushLexicalEnvironmentDirect), m_callFrame, weakPointer(structure), >+ scope, weakPointer(table), m_out.constInt64(JSValue::encode(jsUndefined()))); > ValueFromBlock slowResult = m_out.anchor(callResult); > m_out.jump(continuation); > >Index: Source/JavaScriptCore/ftl/FTLOperations.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLOperations.cpp (revision 186653) >+++ Source/JavaScriptCore/ftl/FTLOperations.cpp (working copy) >@@ -130,7 +130,10 @@ extern "C" JSCell* JIT_OPERATION operati > 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()); > // Figure out what to populate the activation with >Index: Source/JavaScriptCore/interpreter/Interpreter.cpp >=================================================================== >--- Source/JavaScriptCore/interpreter/Interpreter.cpp (revision 186653) >+++ Source/JavaScriptCore/interpreter/Interpreter.cpp (working copy) >@@ -1135,12 +1135,18 @@ JSValue Interpreter::execute(EvalExecuta > } 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: Source/JavaScriptCore/jit/CCallHelpers.h >=================================================================== >--- Source/JavaScriptCore/jit/CCallHelpers.h (revision 186653) >+++ Source/JavaScriptCore/jit/CCallHelpers.h (working copy) >@@ -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: Source/JavaScriptCore/jit/JIT.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JIT.cpp (revision 186653) >+++ Source/JavaScriptCore/jit/JIT.cpp (working copy) >@@ -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) >@@ -273,6 +272,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_push_lexical_environment) >+ DEFINE_OP(op_pop_lexical_environment) > case op_put_by_id_out_of_line: > case op_put_by_id_transition_direct: > case op_put_by_id_transition_normal: >@@ -285,9 +286,6 @@ void JIT::privateCompileMainPass() > DEFINE_OP(op_put_getter_by_id) > DEFINE_OP(op_put_setter_by_id) > DEFINE_OP(op_put_getter_setter) >- case op_init_global_const_nop: >- NEXT_OPCODE(op_init_global_const_nop); >- DEFINE_OP(op_init_global_const) > > DEFINE_OP(op_ret) > DEFINE_OP(op_rshift) >Index: Source/JavaScriptCore/jit/JIT.h >=================================================================== >--- Source/JavaScriptCore/jit/JIT.h (revision 186653) >+++ Source/JavaScriptCore/jit/JIT.h (working copy) >@@ -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*); >@@ -534,13 +533,14 @@ 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_push_lexical_environment(Instruction*); >+ void emit_op_pop_lexical_environment(Instruction*); > void emit_op_put_by_id(Instruction*); > void emit_op_put_by_index(Instruction*); > void emit_op_put_by_val(Instruction*); > void emit_op_put_getter_by_id(Instruction*); > void emit_op_put_setter_by_id(Instruction*); > void emit_op_put_getter_setter(Instruction*); >- void emit_op_init_global_const(Instruction*); > void emit_op_ret(Instruction*); > void emit_op_rshift(Instruction*); > void emit_op_strcat(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: Source/JavaScriptCore/jit/JITInlines.h >=================================================================== >--- Source/JavaScriptCore/jit/JITInlines.h (revision 186653) >+++ Source/JavaScriptCore/jit/JITInlines.h (working copy) >@@ -283,6 +283,12 @@ ALWAYS_INLINE MacroAssembler::Call JIT:: > 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:: > 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: Source/JavaScriptCore/jit/JITOpcodes32_64.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JITOpcodes32_64.cpp (revision 186653) >+++ Source/JavaScriptCore/jit/JITOpcodes32_64.cpp (working copy) >@@ -845,6 +845,20 @@ void JIT::emit_op_catch(Instruction* cur > emitStore(thrownValue, regT1, regT0); > } > >+void JIT::emit_op_push_lexical_environment(Instruction* currentInstruction) >+{ >+ int dst = currentInstruction[1].u.operand; >+ callOperation(operationPushLexicalEnvironment, dst, currentInstruction); >+} >+ >+void JIT::emit_op_pop_lexical_environment(Instruction* currentInstruction) >+{ >+ int scopeSrcAndDst = currentInstruction[1].u.operand; >+ emitLoadPayload(scopeSrcAndDst, regT0); >+ loadPtr(Address(regT0, JSScope::offsetOfNext()), regT0); >+ emitStoreCell(scopeSrcAndDst, regT0); >+} >+ > void JIT::emit_op_switch_imm(Instruction* currentInstruction) > { > size_t tableIndex = currentInstruction[1].u.operand; >@@ -921,17 +935,6 @@ void JIT::emit_op_enter(Instruction* cur > 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: Source/JavaScriptCore/jit/JITOpcodes.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JITOpcodes.cpp (revision 186653) >+++ Source/JavaScriptCore/jit/JITOpcodes.cpp (working copy) >@@ -535,6 +535,20 @@ void JIT::emit_op_catch(Instruction* cur > emitPutVirtualRegister(currentInstruction[2].u.operand); > } > >+void JIT::emit_op_push_lexical_environment(Instruction* currentInstruction) >+{ >+ int dst = currentInstruction[1].u.operand; >+ callOperation(operationPushLexicalEnvironment, dst, currentInstruction); >+} >+ >+void JIT::emit_op_pop_lexical_environment(Instruction* currentInstruction) >+{ >+ int scopeSrcAndDst = currentInstruction[1].u.operand; >+ emitGetVirtualRegister(scopeSrcAndDst, regT0); >+ loadPtr(Address(regT0, JSScope::offsetOfNext()), regT0); >+ emitStoreCell(scopeSrcAndDst, regT0); >+} >+ > void JIT::emit_op_switch_imm(Instruction* currentInstruction) > { > size_t tableIndex = currentInstruction[1].u.operand; >@@ -673,17 +687,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: Source/JavaScriptCore/jit/JITOperations.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JITOperations.cpp (revision 186653) >+++ Source/JavaScriptCore/jit/JITOperations.cpp (working copy) >@@ -1443,6 +1443,21 @@ void JIT_OPERATION operationPushWithScop > exec->uncheckedR(dst) = JSWithScope::create(exec, o, currentScope); > } > >+EncodedJSValue JIT_OPERATION operationPushLexicalEnvironment(ExecState* exec, Instruction* bytecodePC) >+{ >+ VM& vm = exec->vm(); >+ NativeCallFrameTracer tracer(&vm, exec); >+ >+ Instruction* pc = bytecodePC; >+ JSScope* currentScope = exec->uncheckedR(pc[2].u.operand).Register::scope(); >+ SymbolTable* symbolTable = jsCast<SymbolTable*>(exec->r(pc[3].u.operand).jsValue()); >+ JSValue initialValue = exec->r(pc[4].u.operand).jsValue(); >+ ASSERT(initialValue == jsUndefined() || initialValue == jsTDZValue()); >+ JSScope* newScope = JSLexicalEnvironment::create(vm, exec->lexicalGlobalObject(), currentScope, symbolTable, initialValue); >+ >+ return JSValue::encode(newScope); >+} >+ > void JIT_OPERATION operationPopScope(ExecState* exec, int32_t scopeReg) > { > VM& vm = exec->vm(); >@@ -1491,14 +1506,6 @@ EncodedJSValue JIT_OPERATION operationCh > 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: Source/JavaScriptCore/jit/JITOperations.h >=================================================================== >--- Source/JavaScriptCore/jit/JITOperations.h (revision 186653) >+++ Source/JavaScriptCore/jit/JITOperations.h (working copy) >@@ -126,6 +126,7 @@ typedef EncodedJSValue JIT_OPERATION (*J > 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_JITOpe > 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); >@@ -313,11 +314,11 @@ void JIT_OPERATION operationPutGetterSet > void JIT_OPERATION operationPushCatchScope(ExecState*, int32_t, SymbolTable*, EncodedJSValue) WTF_INTERNAL; > void JIT_OPERATION operationPushFunctionNameScope(ExecState*, int32_t, SymbolTable*, EncodedJSValue) WTF_INTERNAL; > void JIT_OPERATION operationPushWithScope(ExecState*, int32_t, EncodedJSValue) WTF_INTERNAL; >+EncodedJSValue JIT_OPERATION operationPushLexicalEnvironment(ExecState*, Instruction*) WTF_INTERNAL; > 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: Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp (revision 186653) >+++ Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp (working copy) >@@ -861,21 +861,6 @@ void JIT::emit_op_put_to_arguments(Instr > store32(regT2, Address(regT0, DirectArguments::storageOffset() + index * sizeof(WriteBarrier<Unknown>) + PayloadOffset)); > } > >-void JIT::emit_op_init_global_const(Instruction* currentInstruction) >-{ >- WriteBarrier<Unknown>* variablePointer = currentInstruction[1].u.variablePointer; >- int value = currentInstruction[2].u.operand; >- >- JSGlobalObject* globalObject = m_codeBlock->globalObject(); >- >- emitWriteBarrier(globalObject, value, ShouldFilterValue); >- >- emitLoad(value, regT1, regT0); >- >- store32(regT1, variablePointer->tagPointer()); >- store32(regT0, variablePointer->payloadPointer()); >-} >- > } // namespace JSC > > #endif // USE(JSVALUE32_64) >Index: Source/JavaScriptCore/jit/JITPropertyAccess.cpp >=================================================================== >--- Source/JavaScriptCore/jit/JITPropertyAccess.cpp (revision 186653) >+++ Source/JavaScriptCore/jit/JITPropertyAccess.cpp (working copy) >@@ -828,14 +828,6 @@ void JIT::emit_op_put_to_arguments(Instr > store64(regT1, Address(regT0, DirectArguments::storageOffset() + index * sizeof(WriteBarrier<Unknown>))); > } > >-void JIT::emit_op_init_global_const(Instruction* currentInstruction) >-{ >- JSGlobalObject* globalObject = m_codeBlock->globalObject(); >- emitWriteBarrier(globalObject, currentInstruction[2].u.operand, ShouldFilterValue); >- emitGetVirtualRegister(currentInstruction[2].u.operand, regT0); >- store64(regT0, currentInstruction[1].u.variablePointer); >-} >- > #endif // USE(JSVALUE64) > > #if USE(JSVALUE64) >Index: Source/JavaScriptCore/llint/LLIntSlowPaths.cpp >=================================================================== >--- Source/JavaScriptCore/llint/LLIntSlowPaths.cpp (revision 186653) >+++ Source/JavaScriptCore/llint/LLIntSlowPaths.cpp (working copy) >@@ -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(); >@@ -1311,6 +1300,27 @@ LLINT_SLOW_PATH_DECL(slow_path_push_name > LLINT_END(); > } > >+LLINT_SLOW_PATH_DECL(slow_path_push_lexical_environment) >+{ >+ LLINT_BEGIN(); >+ int scopeReg = pc[2].u.operand; >+ JSScope* currentScope = exec->uncheckedR(scopeReg).Register::scope(); >+ SymbolTable* symbolTable = jsCast<SymbolTable*>(LLINT_OP_C(3).jsValue()); >+ JSValue initialValue = LLINT_OP_C(4).jsValue(); >+ ASSERT(initialValue == jsUndefined() || initialValue == jsTDZValue()); >+ JSScope* newScope = JSLexicalEnvironment::create(vm, exec->lexicalGlobalObject(), currentScope, symbolTable, initialValue); >+ LLINT_RETURN(newScope); >+} >+ >+LLINT_SLOW_PATH_DECL(slow_path_pop_lexical_environment) >+{ >+ 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_throw) > { > LLINT_BEGIN(); >Index: Source/JavaScriptCore/llint/LLIntSlowPaths.h >=================================================================== >--- Source/JavaScriptCore/llint/LLIntSlowPaths.h (revision 186653) >+++ Source/JavaScriptCore/llint/LLIntSlowPaths.h (working copy) >@@ -62,7 +62,6 @@ LLINT_SLOW_PATH_HIDDEN_DECL(entry_osr_fu > 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); >@@ -110,6 +109,8 @@ LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_to > 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_push_lexical_environment); >+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_pop_lexical_environment); > LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_throw); > LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_throw_static_error); > LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_handle_watchdog_timer); >Index: Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >=================================================================== >--- Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm (revision 186653) >+++ Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm (working copy) >@@ -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: >@@ -1374,17 +1369,6 @@ macro storePropertyAtVariableOffset(prop > end > > >-_llint_op_init_global_const: >- traceExecution() >- writeBarrierOnGlobalObject(2) >- loadi 8[PC], t1 >- loadi 4[PC], t0 >- loadConstantOrVariable(t1, t2, t3) >- storei t2, TagOffset[t0] >- storei t3, PayloadOffset[t0] >- dispatch(5) >- >- > # We only do monomorphic get_by_id caching for now, and we do not modify the > # opcode. We do, however, allow for the cache to change anytime if fails, since > # ping-ponging is free. At best we get lucky and the get_by_id will continue >Index: Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >=================================================================== >--- Source/JavaScriptCore/llint/LowLevelInterpreter64.asm (revision 186653) >+++ Source/JavaScriptCore/llint/LowLevelInterpreter64.asm (working copy) >@@ -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: >@@ -1242,15 +1236,6 @@ macro storePropertyAtVariableOffset(prop > storeq value, (firstOutOfLineOffset - 2) * 8[objectAndStorage, propertyOffsetAsInt, 8] > end > >-_llint_op_init_global_const: >- traceExecution() >- writeBarrierOnGlobalObject(2) >- loadisFromInstruction(2, t1) >- loadpFromInstruction(1, t0) >- loadConstantOrVariable(t1, t2) >- storeq t2, [t0] >- dispatch(5) >- > > macro getById(getPropertyStorage) > traceExecution() >Index: Source/JavaScriptCore/llint/LowLevelInterpreter.asm >=================================================================== >--- Source/JavaScriptCore/llint/LowLevelInterpreter.asm (revision 186653) >+++ Source/JavaScriptCore/llint/LowLevelInterpreter.asm (working copy) >@@ -1299,6 +1299,18 @@ _llint_op_push_name_scope: > dispatch(5) > > >+_llint_op_push_lexical_environment: >+ traceExecution() >+ callSlowPath(_llint_slow_path_push_lexical_environment) >+ dispatch(5) >+ >+ >+_llint_op_pop_lexical_environment: >+ traceExecution() >+ callSlowPath(_llint_slow_path_pop_lexical_environment) >+ dispatch(2) >+ >+ > _llint_op_throw: > traceExecution() > callSlowPath(_llint_slow_path_throw) >@@ -1419,5 +1431,3 @@ macro notSupported() > end > end > >-_llint_op_init_global_const_nop: >- dispatch(5) >Index: Source/JavaScriptCore/parser/ASTBuilder.h >=================================================================== >--- Source/JavaScriptCore/parser/ASTBuilder.h (revision 186653) >+++ Source/JavaScriptCore/parser/ASTBuilder.h (working copy) >@@ -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::makeAssignNo > 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: Source/JavaScriptCore/parser/Keywords.table >=================================================================== >--- Source/JavaScriptCore/parser/Keywords.table (revision 186653) >+++ Source/JavaScriptCore/parser/Keywords.table (working copy) >@@ -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: Source/JavaScriptCore/parser/NodeConstructors.h >=================================================================== >--- Source/JavaScriptCore/parser/NodeConstructors.h (revision 186653) >+++ Source/JavaScriptCore/parser/NodeConstructors.h (working copy) >@@ -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: Source/JavaScriptCore/parser/Nodes.cpp >=================================================================== >--- Source/JavaScriptCore/parser/Nodes.cpp (revision 186653) >+++ Source/JavaScriptCore/parser/Nodes.cpp (working copy) >@@ -94,9 +94,10 @@ ScopeNode::ScopeNode(ParserArena& parser > { > } > >-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& parser > , 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::singleStatemen > > // ------------------------------ 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(Vec > > // ------------------------------ 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(JS > > // ------------------------------ 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(PassRef > m_functionMode = functionMode; > } > >+VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables) >+{ >+ m_lexicalVariables.swap(lexicalVariables); >+} >+ > } // namespace JSC >Index: Source/JavaScriptCore/parser/Nodes.h >=================================================================== >--- Source/JavaScriptCore/parser/Nodes.h (revision 186653) >+++ Source/JavaScriptCore/parser/Nodes.h (working copy) >@@ -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: Source/JavaScriptCore/parser/Parser.cpp >=================================================================== >--- Source/JavaScriptCore/parser/Parser.cpp (revision 186653) >+++ Source/JavaScriptCore/parser/Parser.cpp (working copy) >@@ -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> TreeStateme > { > // 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> TreeStateme > 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> TreeStateme > } > > 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> TreeExpress > 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> TreeExpress > 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> TreeExpress > 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> TreeExpress > 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> TreeDestruc > 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> TreeDestruc > } > } > } >- 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> TreeStateme > #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> TreeDestruc > 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> TreeDestruc > } > > 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> TreeDestruc > 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> TreeDestruc > > 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> TreeDestruc > } > > 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; > } >@@ -819,25 +859,22 @@ template <class TreeBuilder> TreeExpress > template <typename LexerType> > template <class TreeBuilder> TreeConstDeclList Parser<LexerType>::parseConstDeclarationList(TreeBuilder& context) > { >- failIfTrue(strictMode(), "Const declarations are not supported in strict mode"); > TreeConstDeclList constDecls = 0; > TreeConstDeclList tail = 0; > 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)); >- >+ failIfFalse(hasInitializer, "const declarations must be followed with '=' and have an initializer"); >+ // FIXME: This should be LexicalVariable when we support proper ES6 const semantics. >+ declareVariable(name, DeclarationType::LexicalDeclaration, true); > TreeExpression initializer = 0; >- if (hasInitializer) { >- next(TreeBuilder::DontBuildStrings); // consume '=' >- initializer = parseAssignmentExpression(context); >- failIfFalse(!!initializer, "Unable to parse initializer"); >- } >+ next(TreeBuilder::DontBuildStrings); // consume '=' >+ initializer = parseAssignmentExpression(context); >+ failIfFalse(!!initializer, "Unable to parse initializer"); > tail = context.appendConstDecl(location, tail, name, initializer); > if (!constDecls) > constDecls = tail; >@@ -859,17 +896,44 @@ template <class TreeBuilder> TreeStateme > 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 +969,21 @@ template <class TreeBuilder> TreeStateme > 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 +1022,13 @@ template <class TreeBuilder> TreeStateme > 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 +1048,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 +1081,7 @@ template <class TreeBuilder> TreeStateme > 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 +1103,7 @@ template <class TreeBuilder> TreeStateme > 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 +1197,9 @@ template <class TreeBuilder> TreeStateme > > 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 +1212,9 @@ template <class TreeBuilder> TreeStateme > 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 +1286,7 @@ template <class TreeBuilder> TreeStateme > 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 +1294,12 @@ template <class TreeBuilder> TreeStateme > 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 +1330,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 +1361,12 @@ template <class TreeBuilder> TreeStateme > 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 +1387,7 @@ template <class TreeBuilder> TreeStateme > 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 +1594,7 @@ template <class TreeBuilder> bool Parser > > 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 +1682,7 @@ template <class TreeBuilder> bool Parser > cachedInfo->strictMode, constructorKind); > > functionScope->restoreFromSourceProviderCache(cachedInfo); >- failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo), "Parser error"); >+ popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo); > > m_token = cachedInfo->endFunctionToken(); > >@@ -1688,7 +1790,7 @@ template <class TreeBuilder> bool Parser > 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 +1838,8 @@ template <class TreeBuilder> TreeStateme > 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 +1966,7 @@ template <class TreeBuilder> TreeClassEx > } > } > >- 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 +2020,7 @@ template <class TreeBuilder> TreeStateme > 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 +2033,7 @@ template <class TreeBuilder> TreeStateme > 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 +2041,7 @@ template <class TreeBuilder> TreeStateme > 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 +2203,7 @@ template <typename TreeBuilder> TreeExpr > 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 +2468,7 @@ template <class TreeBuilder> TreePropert > { > 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 +2784,7 @@ template <class TreeBuilder> TreeExpress > 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 +2850,10 @@ template <class TreeBuilder> TreeExpress > case TEMPLATE: > return parseTemplateLiteral(context, LexerType::RawStringsBuildMode::DontBuildRawStrings); > #endif >+ case LET: >+ if (!strictMode()) >+ goto identifierExpression; >+ FALLTHROUGH; > default: > failDueToUnexpectedToken(); > } >Index: Source/JavaScriptCore/parser/Parser.h >=================================================================== >--- Source/JavaScriptCore/parser/Parser.h (revision 186653) >+++ Source/JavaScriptCore/parser/Parser.h (working copy) >@@ -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,107 @@ 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) > { >+ 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(); >+ > return isValidStrictMode; > } > >+ bool declareLexicalVariable(const Identifier* ident, bool isConstant) >+ { >+ 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()); >+ if (isConstant) >+ addResult.iterator->value.setIsConstant(); >+ else >+ 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 +304,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 +336,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 +360,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 +376,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 +449,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 +484,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 +497,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 +588,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 +646,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); >+ } >+ >+ 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, isConstant); > } > > 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 +731,7 @@ private: > if (!m_syntaxAlreadyValidated || strictMode()) > m_scopeStack.last().declareWrite(ident); > } >- >+ > ScopeStack m_scopeStack; > > const SourceProviderCacheItem* findCachedFunctionInfo(int openBracePos) >@@ -565,8 +742,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 +929,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 +942,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 +954,7 @@ private: > template <class TreeBuilder> TreeStatement parseClassDeclaration(TreeBuilder&); > #endif > template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&); >- template <class TreeBuilder> TreeStatement parseVarDeclaration(TreeBuilder&); >+ template <class TreeBuilder> TreeStatement parseVariableDeclaration(TreeBuilder&, DeclarationType); > template <class TreeBuilder> TreeStatement parseConstDeclaration(TreeBuilder&); > template <class TreeBuilder> TreeStatement parseDoWhileStatement(TreeBuilder&); > template <class TreeBuilder> TreeStatement parseWhileStatement(TreeBuilder&); >@@ -810,7 +991,7 @@ private: > template <class TreeBuilder> ALWAYS_INLINE TreeFunctionBody parseFunctionBody(TreeBuilder&, int functionKeywordStart, int functionNameStart, int parametersStart, ConstructorKind, FunctionParseType); > template <class TreeBuilder> ALWAYS_INLINE TreeFormalParameterList parseFormalParameters(TreeBuilder&); > enum VarDeclarationListContext { ForLoopContext, VarDeclarationContext }; >- template <class TreeBuilder> TreeExpression parseVarDeclarationList(TreeBuilder&, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext); >+ template <class TreeBuilder> TreeExpression parseVariableDeclarationList(TreeBuilder&, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext, DeclarationType); > template <class TreeBuilder> NEVER_INLINE TreeConstDeclList parseConstDeclarationList(TreeBuilder&); > > #if ENABLE(ES6_ARROWFUNCTION_SYNTAX) >@@ -818,9 +999,9 @@ private: > template <class TreeBuilder> TreeExpression parseArrowFunctionExpression(TreeBuilder&); > #endif > >- template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createBindingPattern(TreeBuilder&, DestructuringKind, const Identifier&, int depth, JSToken); >- template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseDestructuringPattern(TreeBuilder&, DestructuringKind, int depth = 0); >- template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern tryParseDestructuringPatternExpression(TreeBuilder&); >+ template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createBindingPattern(TreeBuilder&, DestructuringKind, const Identifier&, int depth, JSToken, AssignmentContext); >+ template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseDestructuringPattern(TreeBuilder&, DestructuringKind, AssignmentContext = AssignmentContext::DeclarationStatement, int depth = 0); >+ template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern tryParseDestructuringPatternExpression(TreeBuilder&, AssignmentContext); > template <class TreeBuilder> NEVER_INLINE TreeExpression parseDefaultValueForDestructuringPattern(TreeBuilder&); > > template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionRequirements, FunctionParseMode, bool nameIsInContainingScope, ConstructorKind, SuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&, FunctionParseType); >@@ -946,9 +1127,8 @@ private: > bool m_parsingBuiltin; > ConstructorKind m_defaultConstructorKind; > ThisTDZMode m_thisTDZMode; >- DeclarationStacks::VarStack m_varDeclarations; >+ VariableEnvironment m_varDeclarations; > DeclarationStacks::FunctionStack m_funcDeclarations; >- IdentifierSet m_capturedVariables; > Vector<RefPtr<UniquedStringImpl>> m_closedVariables; > CodeFeatures m_features; > int m_numConstants; >@@ -1020,7 +1200,7 @@ std::unique_ptr<ParsedNode> Parser<Lexer > m_sourceElements, > m_varDeclarations, > m_funcDeclarations, >- m_capturedVariables, >+ currentScope()->finalizeLexicalEnvironment(), > *m_source, > m_features, > m_numConstants); >Index: Source/JavaScriptCore/parser/ParserTokens.h >=================================================================== >--- Source/JavaScriptCore/parser/ParserTokens.h (revision 186653) >+++ Source/JavaScriptCore/parser/ParserTokens.h (working copy) >@@ -57,6 +57,7 @@ enum JSTokenType { > FOR, > NEW, > VAR, >+ LET, > CONSTTOKEN, > CONTINUE, > FUNCTION, >Index: Source/JavaScriptCore/parser/SyntaxChecker.h >=================================================================== >--- Source/JavaScriptCore/parser/SyntaxChecker.h (revision 186653) >+++ Source/JavaScriptCore/parser/SyntaxChecker.h (working copy) >@@ -172,8 +172,9 @@ public: > ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int, int, int) { return NewExpr; } > ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int) { return NewExpr; } > ExpressionType createConditionalExpr(const JSTokenLocation&, ExpressionType, ExpressionType, ExpressionType) { return ConditionalExpr; } >- ExpressionType createAssignResolve(const JSTokenLocation&, const Identifier&, ExpressionType, int, int, int) { return AssignmentExpr; } >+ ExpressionType createAssignResolve(const JSTokenLocation&, const Identifier&, ExpressionType, int, int, int, AssignmentContext) { return AssignmentExpr; } > ExpressionType createEmptyVarExpression(const JSTokenLocation&, const Identifier&) { return AssignmentExpr; } >+ ExpressionType createEmptyLetExpression(const JSTokenLocation&, const Identifier&) { return AssignmentExpr; } > #if ENABLE(ES6_CLASS_SYNTAX) > ClassExpression createClassExpr(const JSTokenLocation&, const Identifier&, ExpressionType, ExpressionType, PropertyList, PropertyList) { return ClassExpr; } > #endif >@@ -230,22 +231,23 @@ public: > int createClassDeclStatement(const JSTokenLocation&, ClassExpression, > const JSTextPosition&, const JSTextPosition&, int, int) { return StatementResult; } > #endif >- int createBlockStatement(const JSTokenLocation&, int, int, int) { return StatementResult; } >+ int createBlockStatement(const JSTokenLocation&, int, int, int, VariableEnvironment&) { return StatementResult; } > int createExprStatement(const JSTokenLocation&, int, int, int) { return StatementResult; } > int createIfStatement(const JSTokenLocation&, int, int, int, int) { return StatementResult; } > int createIfStatement(const JSTokenLocation&, int, int, int, int, int) { return StatementResult; } >- int createForLoop(const JSTokenLocation&, int, int, int, int, int, int) { return StatementResult; } >- int createForInLoop(const JSTokenLocation&, int, int, int, int, int, int, int, int) { return StatementResult; } >- int createForOfLoop(const JSTokenLocation&, int, int, int, int, int, int, int, int) { return StatementResult; } >+ int createForLoop(const JSTokenLocation&, int, int, int, int, int, int, VariableEnvironment&) { return StatementResult; } >+ int createForInLoop(const JSTokenLocation&, int, int, int, int, int, int, int, int, VariableEnvironment&) { return StatementResult; } >+ int createForOfLoop(const JSTokenLocation&, int, int, int, int, int, int, int, int, VariableEnvironment&) { return StatementResult; } > int createEmptyStatement(const JSTokenLocation&) { return StatementResult; } > int createVarStatement(const JSTokenLocation&, int, int, int) { return StatementResult; } >+ int createLetStatement(const JSTokenLocation&, int, int, int) { return StatementResult; } > int createReturnStatement(const JSTokenLocation&, int, int, int) { return StatementResult; } > int createBreakStatement(const JSTokenLocation&, int, int) { return StatementResult; } > int createBreakStatement(const JSTokenLocation&, const Identifier*, int, int) { return StatementResult; } > int createContinueStatement(const JSTokenLocation&, int, int) { return StatementResult; } > int createContinueStatement(const JSTokenLocation&, const Identifier*, int, int) { return StatementResult; } > int createTryStatement(const JSTokenLocation&, int, const Identifier*, int, int, int, int) { return StatementResult; } >- int createSwitchStatement(const JSTokenLocation&, int, int, int, int, int, int) { return StatementResult; } >+ int createSwitchStatement(const JSTokenLocation&, int, int, int, int, int, int, VariableEnvironment&) { return StatementResult; } > int createWhileStatement(const JSTokenLocation&, int, int, int, int) { return StatementResult; } > int createWithStatement(const JSTokenLocation&, int, int, int, int, int, int) { return StatementResult; } > int createDoWhileStatement(const JSTokenLocation&, int, int, int, int) { return StatementResult; } >@@ -269,7 +271,6 @@ public: > } > > void appendStatement(int, int) { } >- void addVar(const Identifier*, bool) { } > int combineCommaNodes(const JSTokenLocation&, int, int) { return CommaExpr; } > int evalCount() const { return 0; } > void appendBinaryExpressionInfo(int& operandStackDepth, int expr, int, int, int, bool) >@@ -328,7 +329,7 @@ public: > void appendObjectPatternEntry(ArrayPattern, const JSTokenLocation&, bool, const Identifier&, DestructuringPattern, int) > { > } >- DestructuringPattern createBindingLocation(const JSTokenLocation&, const Identifier&, const JSTextPosition&, const JSTextPosition&) >+ DestructuringPattern createBindingLocation(const JSTokenLocation&, const Identifier&, const JSTextPosition&, const JSTextPosition&, AssignmentContext) > { > return BindingDestructuring; > } >Index: Source/JavaScriptCore/parser/VariableEnvironment.cpp >=================================================================== >--- Source/JavaScriptCore/parser/VariableEnvironment.cpp (revision 0) >+++ Source/JavaScriptCore/parser/VariableEnvironment.cpp (working copy) >@@ -0,0 +1,84 @@ >+/* >+ * Copyright (C) 2015 Apple Inc. All Rights Reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "VariableEnvironment.h" >+ >+namespace JSC { >+ >+void VariableEnvironment::markVariableAsCapturedIfDefined(const RefPtr<UniquedStringImpl>& identifier) >+{ >+ auto findResult = m_map.find(identifier); >+ if (findResult != m_map.end()) >+ findResult->value.setIsCaptured(); >+} >+ >+void VariableEnvironment::markVariableAsCaptured(const RefPtr<UniquedStringImpl>& identifier) >+{ >+ auto findResult = m_map.find(identifier); >+ RELEASE_ASSERT(findResult != m_map.end()); >+ findResult->value.setIsCaptured(); >+} >+ >+void VariableEnvironment::markAllVariablesAsCaptured() >+{ >+ if (m_isEverythingCaptured) >+ return; >+ >+ m_isEverythingCaptured = true; // For fast queries. >+ // We must mark every entry as captured for when we iterate through m_map and entry.isCaptured() is called. >+ for (auto iter = m_map.begin(), end = m_map.end(); iter != end; ++iter) >+ iter->value.setIsCaptured(); >+} >+ >+bool VariableEnvironment::hasCapturedVariables() const >+{ >+ if (m_isEverythingCaptured) >+ return size() > 0; >+ for (auto entry : m_map) { >+ if (entry.value.isCaptured()) >+ return true; >+ } >+ return false; >+} >+ >+bool VariableEnvironment::captures(UniquedStringImpl* identifier) const >+{ >+ if (m_isEverythingCaptured) >+ return true; >+ >+ auto findResult = m_map.find(identifier); >+ if (findResult == m_map.end()) >+ return false; >+ return findResult->value.isCaptured(); >+} >+ >+void VariableEnvironment::swap(VariableEnvironment& other) >+{ >+ m_map.swap(other.m_map); >+ m_isEverythingCaptured = other.m_isEverythingCaptured; >+} >+ >+} // namespace JSC >Index: Source/JavaScriptCore/parser/VariableEnvironment.h >=================================================================== >--- Source/JavaScriptCore/parser/VariableEnvironment.h (revision 0) >+++ Source/JavaScriptCore/parser/VariableEnvironment.h (working copy) >@@ -0,0 +1,90 @@ >+/* >+ * Copyright (C) 2015 Apple Inc. All Rights Reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef VariableEnvironment_h >+#define VariableEnvironment_h >+ >+#include "Identifier.h" >+#include <wtf/HashMap.h> >+#include <wtf/text/UniquedStringImpl.h> >+ >+namespace JSC { >+ >+struct VariableEnvironmentEntry { >+public: >+ ALWAYS_INLINE bool isCaptured() const { return m_bits & IsCaptured; } >+ ALWAYS_INLINE bool isConstant() const { return m_bits & IsConstant; } >+ ALWAYS_INLINE bool isVar() const { return m_bits & IsVar; } >+ ALWAYS_INLINE bool isLet() const { return m_bits & IsLet; } >+ >+ ALWAYS_INLINE void setIsCaptured() { m_bits |= IsCaptured; } >+ ALWAYS_INLINE void setIsConstant() { m_bits |= IsConstant; } >+ ALWAYS_INLINE void setIsVar() { m_bits |= IsVar; } >+ ALWAYS_INLINE void setIsLet() { m_bits |= IsLet; } >+ >+ ALWAYS_INLINE void clearIsVar() { m_bits &= ~IsVar; } >+ >+private: >+ enum Traits { >+ IsCaptured = 1 << 0, >+ IsConstant = 1 << 1, >+ IsVar = 1 << 2, >+ IsLet = 1 << 3 >+ }; >+ uint8_t m_bits { 0 }; >+}; >+ >+struct VariableEnvironmentEntryHashTraits : HashTraits<VariableEnvironmentEntry> { >+ static const bool needsDestruction = false; >+}; >+ >+class VariableEnvironment { >+private: >+ typedef HashMap<RefPtr<UniquedStringImpl>, VariableEnvironmentEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, VariableEnvironmentEntryHashTraits> Map; >+public: >+ ALWAYS_INLINE Map::iterator begin() { return m_map.begin(); } >+ ALWAYS_INLINE Map::iterator end() { return m_map.end(); } >+ ALWAYS_INLINE Map::const_iterator begin() const { return m_map.begin(); } >+ ALWAYS_INLINE Map::const_iterator end() const { return m_map.end(); } >+ ALWAYS_INLINE Map::AddResult add(const RefPtr<UniquedStringImpl>& identifier) { return m_map.add(identifier, VariableEnvironmentEntry()); } >+ ALWAYS_INLINE Map::AddResult add(const Identifier& identifier) { return add(identifier.impl()); } >+ ALWAYS_INLINE unsigned size() const { return m_map.size(); } >+ ALWAYS_INLINE bool contains(const RefPtr<UniquedStringImpl>& identifier) const { return m_map.contains(identifier); } >+ ALWAYS_INLINE bool remove(const RefPtr<UniquedStringImpl>& identifier) { return m_map.remove(identifier); } >+ void swap(VariableEnvironment& other); >+ void markVariableAsCapturedIfDefined(const RefPtr<UniquedStringImpl>& identifier); >+ void markVariableAsCaptured(const RefPtr<UniquedStringImpl>& identifier); >+ void markAllVariablesAsCaptured(); >+ bool hasCapturedVariables() const; >+ bool captures(UniquedStringImpl* identifier) const; >+ >+private: >+ Map m_map; >+ bool m_isEverythingCaptured { false }; >+}; >+ >+} // namespace JSC >+ >+#endif // VariableEnvironment_h >Index: Source/JavaScriptCore/runtime/CodeCache.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/CodeCache.cpp (revision 186653) >+++ Source/JavaScriptCore/runtime/CodeCache.cpp (working copy) >@@ -76,7 +76,7 @@ template <> struct CacheTypes<UnlinkedEv > > template <class UnlinkedCodeBlockType, class ExecutableType> > UnlinkedCodeBlockType* CodeCache::getGlobalCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, >- JSParserStrictMode strictMode, ThisTDZMode thisTDZMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) >+ JSParserStrictMode strictMode, ThisTDZMode thisTDZMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error, const VariableEnvironment* variablesUnderTDZ) > { > SourceCodeKey key = SourceCodeKey(source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, builtinMode, strictMode, thisTDZMode); > SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key); >@@ -109,7 +109,7 @@ UnlinkedCodeBlockType* CodeCache::getGlo > UnlinkedCodeBlockType* unlinkedCodeBlock = UnlinkedCodeBlockType::create(&vm, executable->executableInfo()); > unlinkedCodeBlock->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->firstLine() - source.firstLine(), lineCount, unlinkedEndColumn); > >- auto generator = std::make_unique<BytecodeGenerator>(vm, rootNode.get(), unlinkedCodeBlock, debuggerMode, profilerMode); >+ auto generator = std::make_unique<BytecodeGenerator>(vm, rootNode.get(), unlinkedCodeBlock, debuggerMode, profilerMode, variablesUnderTDZ); > error = generator->generate(); > if (error.isValid()) > return nullptr; >@@ -123,12 +123,13 @@ UnlinkedCodeBlockType* CodeCache::getGlo > > UnlinkedProgramCodeBlock* CodeCache::getProgramCodeBlock(VM& vm, ProgramExecutable* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) > { >- return getGlobalCodeBlock<UnlinkedProgramCodeBlock>(vm, executable, source, builtinMode, strictMode, ThisTDZMode::CheckIfNeeded, debuggerMode, profilerMode, error); >+ VariableEnvironment emptyParentTDZVariables; >+ return getGlobalCodeBlock<UnlinkedProgramCodeBlock>(vm, executable, source, builtinMode, strictMode, ThisTDZMode::CheckIfNeeded, debuggerMode, profilerMode, error, &emptyParentTDZVariables); > } > >-UnlinkedEvalCodeBlock* CodeCache::getEvalCodeBlock(VM& vm, EvalExecutable* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, ThisTDZMode thisTDZMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) >+UnlinkedEvalCodeBlock* CodeCache::getEvalCodeBlock(VM& vm, EvalExecutable* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, ThisTDZMode thisTDZMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error, const VariableEnvironment* variablesUnderTDZ) > { >- return getGlobalCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, builtinMode, strictMode, thisTDZMode, debuggerMode, profilerMode, error); >+ return getGlobalCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, builtinMode, strictMode, thisTDZMode, debuggerMode, profilerMode, error, variablesUnderTDZ); > } > > // FIXME: There's no need to add the function's name to the key here. It's already in the source code. >@@ -171,7 +172,9 @@ UnlinkedFunctionExecutable* CodeCache::g > return nullptr; > > body->setEndPosition(positionBeforeLastNewline); >- UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, body, UnlinkedNormalFunction); >+ // The Function constructor only has access to global variables, so no variables will be under TDZ. >+ VariableEnvironment emptyTDZVariables; >+ UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, body, UnlinkedNormalFunction, emptyTDZVariables); > functionExecutable->m_nameValue.set(vm, functionExecutable, jsString(&vm, name.string())); > > m_sourceCode.addCache(key, SourceCodeValue(vm, functionExecutable, m_sourceCode.age())); >Index: Source/JavaScriptCore/runtime/CodeCache.h >=================================================================== >--- Source/JavaScriptCore/runtime/CodeCache.h (revision 186653) >+++ Source/JavaScriptCore/runtime/CodeCache.h (working copy) >@@ -30,6 +30,7 @@ > #include "ParserModes.h" > #include "SourceCode.h" > #include "Strong.h" >+#include "VariableEnvironment.h" > #include "WeakRandom.h" > #include <wtf/CurrentTime.h> > #include <wtf/Forward.h> >@@ -255,7 +256,7 @@ public: > ~CodeCache(); > > UnlinkedProgramCodeBlock* getProgramCodeBlock(VM&, ProgramExecutable*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, DebuggerMode, ProfilerMode, ParserError&); >- UnlinkedEvalCodeBlock* getEvalCodeBlock(VM&, EvalExecutable*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, ThisTDZMode, DebuggerMode, ProfilerMode, ParserError&); >+ UnlinkedEvalCodeBlock* getEvalCodeBlock(VM&, EvalExecutable*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, ThisTDZMode, DebuggerMode, ProfilerMode, ParserError&, const VariableEnvironment*); > UnlinkedFunctionExecutable* getFunctionExecutableFromGlobalCode(VM&, const Identifier&, const SourceCode&, ParserError&); > > void clear() >@@ -265,7 +266,7 @@ public: > > private: > template <class UnlinkedCodeBlockType, class ExecutableType> >- UnlinkedCodeBlockType* getGlobalCodeBlock(VM&, ExecutableType*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, ThisTDZMode, DebuggerMode, ProfilerMode, ParserError&); >+ UnlinkedCodeBlockType* getGlobalCodeBlock(VM&, ExecutableType*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, ThisTDZMode, DebuggerMode, ProfilerMode, ParserError&, const VariableEnvironment*); > > CodeCacheMap m_sourceCode; > }; >Index: Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/CommonSlowPaths.cpp (revision 186653) >+++ Source/JavaScriptCore/runtime/CommonSlowPaths.cpp (working copy) >@@ -268,7 +268,7 @@ SLOW_PATH_DECL(slow_path_to_this) > SLOW_PATH_DECL(slow_path_throw_tdz_error) > { > BEGIN(); >- THROW(createReferenceError(exec, "Cannot access uninitialized variable.")); >+ THROW(createTDZError(exec)); > } > > SLOW_PATH_DECL(slow_path_not) >Index: Source/JavaScriptCore/runtime/ExceptionHelpers.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/ExceptionHelpers.cpp (revision 186653) >+++ Source/JavaScriptCore/runtime/ExceptionHelpers.cpp (working copy) >@@ -267,6 +267,11 @@ JSObject* createErrorForInvalidGlobalAss > return createReferenceError(exec, makeString("Strict mode forbids implicit creation of global property '", propertyName, '\'')); > } > >+JSObject* createTDZError(ExecState* exec) >+{ >+ return createReferenceError(exec, "Cannot access uninitialized variable."); >+} >+ > JSObject* throwOutOfMemoryError(ExecState* exec) > { > return exec->vm().throwException(exec, createOutOfMemoryError(exec)); >Index: Source/JavaScriptCore/runtime/ExceptionHelpers.h >=================================================================== >--- Source/JavaScriptCore/runtime/ExceptionHelpers.h (revision 186653) >+++ Source/JavaScriptCore/runtime/ExceptionHelpers.h (working copy) >@@ -41,6 +41,7 @@ JS_EXPORT_PRIVATE bool isTerminatedExecu > JS_EXPORT_PRIVATE JSObject* createError(ExecState*, JSValue, const String&, ErrorInstance::SourceAppender); > JS_EXPORT_PRIVATE JSObject* createStackOverflowError(ExecState*); > JSObject* createUndefinedVariableError(ExecState*, const Identifier&); >+JSObject* createTDZError(ExecState*); > JSObject* createNotAnObjectError(ExecState*, JSValue); > JSObject* createInvalidFunctionApplyParameterError(ExecState*, JSValue); > JSObject* createInvalidInParameterError(ExecState*, JSValue); >Index: Source/JavaScriptCore/runtime/Executable.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/Executable.cpp (revision 186653) >+++ Source/JavaScriptCore/runtime/Executable.cpp (working copy) >@@ -347,7 +347,7 @@ JSObject* ScriptExecutable::prepareForEx > > const ClassInfo EvalExecutable::s_info = { "EvalExecutable", &ScriptExecutable::s_info, 0, CREATE_METHOD_TABLE(EvalExecutable) }; > >-EvalExecutable* EvalExecutable::create(ExecState* exec, const SourceCode& source, bool isInStrictContext, ThisTDZMode thisTDZMode) >+EvalExecutable* EvalExecutable::create(ExecState* exec, const SourceCode& source, bool isInStrictContext, ThisTDZMode thisTDZMode, const VariableEnvironment* variablesUnderTDZ) > { > JSGlobalObject* globalObject = exec->lexicalGlobalObject(); > if (!globalObject->evalEnabled()) { >@@ -358,7 +358,7 @@ EvalExecutable* EvalExecutable::create(E > EvalExecutable* executable = new (NotNull, allocateCell<EvalExecutable>(*exec->heap())) EvalExecutable(exec, source, isInStrictContext); > executable->finishCreation(exec->vm()); > >- UnlinkedEvalCodeBlock* unlinkedEvalCode = globalObject->createEvalCodeBlock(exec, executable, thisTDZMode); >+ UnlinkedEvalCodeBlock* unlinkedEvalCode = globalObject->createEvalCodeBlock(exec, executable, thisTDZMode, variablesUnderTDZ); > if (!unlinkedEvalCode) > return 0; > >@@ -509,8 +509,6 @@ JSObject* ProgramExecutable::initializeG > > BatchedTransitionOptimizer optimizer(vm, globalObject); > >- const UnlinkedProgramCodeBlock::VariableDeclations& variableDeclarations = unlinkedCodeBlock->variableDeclarations(); >- > for (size_t i = 0, numberOfFunctions = unlinkedCodeBlock->numberOfFunctionDecls(); i < numberOfFunctions; ++i) { > UnlinkedFunctionExecutable* unlinkedFunctionExecutable = unlinkedCodeBlock->functionDecl(i); > ASSERT(!unlinkedFunctionExecutable->name().isEmpty()); >@@ -522,11 +520,10 @@ JSObject* ProgramExecutable::initializeG > } > } > >- for (size_t i = 0; i < variableDeclarations.size(); ++i) { >- if (variableDeclarations[i].second & DeclarationStacks::IsConstant) >- globalObject->addConst(callFrame, variableDeclarations[i].first); >- else >- globalObject->addVar(callFrame, variableDeclarations[i].first); >+ const VariableEnvironment& variableDeclarations = unlinkedCodeBlock->variableDeclarations(); >+ for (auto& entry : variableDeclarations) { >+ ASSERT(entry.value.isVar()); >+ globalObject->addVar(callFrame, Identifier::fromUid(&vm, entry.key.get())); > } > return 0; > } >Index: Source/JavaScriptCore/runtime/Executable.h >=================================================================== >--- Source/JavaScriptCore/runtime/Executable.h (revision 186653) >+++ Source/JavaScriptCore/runtime/Executable.h (working copy) >@@ -454,7 +454,7 @@ public: > return m_evalCodeBlock.get(); > } > >- static EvalExecutable* create(ExecState*, const SourceCode&, bool isInStrictContext, ThisTDZMode); >+ static EvalExecutable* create(ExecState*, const SourceCode&, bool isInStrictContext, ThisTDZMode, const VariableEnvironment*); > > PassRefPtr<JITCode> generatedJITCode() > { >Index: Source/JavaScriptCore/runtime/JSCJSValue.h >=================================================================== >--- Source/JavaScriptCore/runtime/JSCJSValue.h (revision 186653) >+++ Source/JavaScriptCore/runtime/JSCJSValue.h (working copy) >@@ -487,6 +487,11 @@ inline JSValue jsUndefined() > return JSValue(JSValue::JSUndefined); > } > >+inline JSValue jsTDZValue() >+{ >+ return JSValue(); >+} >+ > inline JSValue jsBoolean(bool b) > { > return b ? JSValue(JSValue::JSTrue) : JSValue(JSValue::JSFalse); >Index: Source/JavaScriptCore/runtime/JSEnvironmentRecord.h >=================================================================== >--- Source/JavaScriptCore/runtime/JSEnvironmentRecord.h (revision 186653) >+++ Source/JavaScriptCore/runtime/JSEnvironmentRecord.h (working copy) >@@ -100,12 +100,13 @@ protected: > Base::finishCreation(vm); > } > >- void finishCreation(VM& vm) >+ void finishCreation(VM& vm, JSValue value) > { > finishCreationUninitialized(vm); >+ ASSERT(value == jsUndefined() || value == jsTDZValue()); > for (unsigned i = symbolTable()->scopeSize(); i--;) { >- // Filling this with undefined is useful because that's what variables start out as. >- variableAt(ScopeOffset(i)).setUndefined(); >+ // Filling this with undefined/TDZEmptyValue is useful because that's what variables start out as. >+ variableAt(ScopeOffset(i)).setStartingValue(value); > } > } > >Index: Source/JavaScriptCore/runtime/JSGlobalObject.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/JSGlobalObject.cpp (revision 186653) >+++ Source/JavaScriptCore/runtime/JSGlobalObject.cpp (working copy) >@@ -570,7 +570,7 @@ bool JSGlobalObject::defineOwnProperty(J > return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow); > } > >-void JSGlobalObject::addGlobalVar(const Identifier& ident, ConstantMode constantMode) >+void JSGlobalObject::addGlobalVar(const Identifier& ident) > { > ConcurrentJITLocker locker(symbolTable()->m_lock); > SymbolTableEntry entry = symbolTable()->get(locker, ident.impl()); >@@ -578,11 +578,8 @@ void JSGlobalObject::addGlobalVar(const > return; > > ScopeOffset offset = symbolTable()->takeNextScopeOffset(locker); >- SymbolTableEntry newEntry(VarOffset(offset), (constantMode == IsConstant) ? ReadOnly : 0); >- if (constantMode == IsVariable) >- newEntry.prepareToWatch(); >- else >- newEntry.disableWatching(); >+ SymbolTableEntry newEntry(VarOffset(offset), 0); >+ newEntry.prepareToWatch(); > symbolTable()->add(locker, ident.impl(), newEntry); > > ScopeOffset offsetForAssert = addVariables(1); >@@ -593,7 +590,7 @@ void JSGlobalObject::addFunction(ExecSta > { > VM& vm = exec->vm(); > removeDirect(vm, propertyName); // Newly declared functions overwrite existing properties. >- addGlobalVar(propertyName, IsVariable); >+ addGlobalVar(propertyName); > } > > static inline JSObject* lastInPrototypeChain(JSObject* object) >@@ -923,14 +920,14 @@ UnlinkedProgramCodeBlock* JSGlobalObject > return unlinkedCodeBlock; > } > >-UnlinkedEvalCodeBlock* JSGlobalObject::createEvalCodeBlock(CallFrame* callFrame, EvalExecutable* executable, ThisTDZMode thisTDZMode) >+UnlinkedEvalCodeBlock* JSGlobalObject::createEvalCodeBlock(CallFrame* callFrame, EvalExecutable* executable, ThisTDZMode thisTDZMode, const VariableEnvironment* variablesUnderTDZ) > { > ParserError error; > JSParserStrictMode strictMode = executable->isStrictMode() ? JSParserStrictMode::Strict : JSParserStrictMode::NotStrict; > DebuggerMode debuggerMode = hasDebugger() ? DebuggerOn : DebuggerOff; > ProfilerMode profilerMode = hasProfiler() ? ProfilerOn : ProfilerOff; > UnlinkedEvalCodeBlock* unlinkedCodeBlock = vm().codeCache()->getEvalCodeBlock( >- vm(), executable, executable->source(), JSParserBuiltinMode::NotBuiltin, strictMode, thisTDZMode, debuggerMode, profilerMode, error); >+ vm(), executable, executable->source(), JSParserBuiltinMode::NotBuiltin, strictMode, thisTDZMode, debuggerMode, profilerMode, error, variablesUnderTDZ); > > if (hasDebugger()) > debugger()->sourceParsed(callFrame, executable->source().provider(), error.line(), error.message()); >Index: Source/JavaScriptCore/runtime/JSGlobalObject.h >=================================================================== >--- Source/JavaScriptCore/runtime/JSGlobalObject.h (revision 186653) >+++ Source/JavaScriptCore/runtime/JSGlobalObject.h (working copy) >@@ -23,7 +23,6 @@ > #define JSGlobalObject_h > > #include "ArrayAllocationProfile.h" >-#include "ConstantMode.h" > #include "JSArray.h" > #include "JSArrayBufferPrototype.h" > #include "JSClassRef.h" >@@ -39,6 +38,7 @@ > #include "SymbolPrototype.h" > #include "TemplateRegistry.h" > #include "VM.h" >+#include "VariableEnvironment.h" > #include "Watchpoint.h" > #include <JavaScriptCore/JSBase.h> > #include <array> >@@ -356,7 +356,7 @@ protected: > setGlobalThis(vm, thisValue); > } > >- void addGlobalVar(const Identifier&, ConstantMode); >+ void addGlobalVar(const Identifier&); > > public: > JS_EXPORT_PRIVATE ~JSGlobalObject(); >@@ -381,12 +381,7 @@ public: > void addVar(ExecState* exec, const Identifier& propertyName) > { > if (!hasProperty(exec, propertyName)) >- addGlobalVar(propertyName, IsVariable); >- } >- void addConst(ExecState* exec, const Identifier& propertyName) >- { >- if (!hasProperty(exec, propertyName)) >- addGlobalVar(propertyName, IsConstant); >+ addGlobalVar(propertyName); > } > void addFunction(ExecState*, const Identifier&); > >@@ -639,7 +634,7 @@ public: > unsigned weakRandomInteger() { return m_weakRandom.getUint32(); } > > UnlinkedProgramCodeBlock* createProgramCodeBlock(CallFrame*, ProgramExecutable*, JSObject** exception); >- UnlinkedEvalCodeBlock* createEvalCodeBlock(CallFrame*, EvalExecutable*, ThisTDZMode); >+ UnlinkedEvalCodeBlock* createEvalCodeBlock(CallFrame*, EvalExecutable*, ThisTDZMode, const VariableEnvironment*); > > protected: > struct GlobalPropertyInfo { >Index: Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp (revision 186653) >+++ Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp (working copy) >@@ -580,7 +580,8 @@ EncodedJSValue JSC_HOST_CALL globalFuncE > } > > JSGlobalObject* calleeGlobalObject = exec->callee()->globalObject(); >- EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false, ThisTDZMode::CheckIfNeeded); >+ VariableEnvironment emptyTDZVariables; // Indirect eval does not have access to the lexical scope. >+ EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false, ThisTDZMode::CheckIfNeeded, &emptyTDZVariables); > if (!eval) > return JSValue::encode(jsUndefined()); > >Index: Source/JavaScriptCore/runtime/JSLexicalEnvironment.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/JSLexicalEnvironment.cpp (revision 186653) >+++ Source/JavaScriptCore/runtime/JSLexicalEnvironment.cpp (working copy) >@@ -51,23 +51,8 @@ inline bool JSLexicalEnvironment::symbol > if (!isValid(offset)) > return false; > >- slot.setValue(this, DontEnum, variableAt(offset).get()); >- return true; >-} >- >-inline bool JSLexicalEnvironment::symbolTableGet(PropertyName propertyName, PropertyDescriptor& descriptor) >-{ >- SymbolTableEntry entry = symbolTable()->inlineGet(propertyName.uid()); >- if (entry.isNull()) >- return false; >- >- ScopeOffset offset = entry.scopeOffset(); >- >- // Defend against the inspector asking for a var after it has been optimized out. >- if (!isValid(offset)) >- return false; >- >- descriptor.setDescriptor(variableAt(offset).get(), entry.getAttributes()); >+ JSValue result = variableAt(offset).get(); >+ slot.setValue(this, DontEnum, result); > return true; > } > >@@ -85,7 +70,7 @@ inline bool JSLexicalEnvironment::symbol > return false; > ASSERT(!iter->value.isNull()); > if (iter->value.isReadOnly()) { >- if (shouldThrow) >+ if (shouldThrow || isLexicalScope()) > throwTypeError(exec, StrictModeReadonlyPropertyWriteError); > return true; > } >Index: Source/JavaScriptCore/runtime/JSLexicalEnvironment.h >=================================================================== >--- Source/JavaScriptCore/runtime/JSLexicalEnvironment.h (revision 186653) >+++ Source/JavaScriptCore/runtime/JSLexicalEnvironment.h (working copy) >@@ -47,23 +47,21 @@ public: > static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames; > > static JSLexicalEnvironment* create( >- VM& vm, Structure* structure, JSScope* currentScope, SymbolTable* symbolTable) >+ VM& vm, Structure* structure, JSScope* currentScope, SymbolTable* symbolTable, JSValue initialValue) > { > JSLexicalEnvironment* result = > new ( > NotNull, > allocateCell<JSLexicalEnvironment>(vm.heap, allocationSize(symbolTable))) > JSLexicalEnvironment(vm, structure, currentScope, symbolTable); >- result->finishCreation(vm); >+ result->finishCreation(vm, initialValue); > return result; > } >- >- static JSLexicalEnvironment* create(VM& vm, CallFrame* callFrame, JSScope* currentScope, CodeBlock* codeBlock) >+ >+ static JSLexicalEnvironment* create(VM& vm, JSGlobalObject* globalObject, JSScope* currentScope, SymbolTable* symbolTable, JSValue initialValue) > { >- JSGlobalObject* globalObject = callFrame->lexicalGlobalObject(); > Structure* structure = globalObject->activationStructure(); >- SymbolTable* symbolTable = codeBlock->symbolTable(); >- return create(vm, structure, currentScope, symbolTable); >+ return create(vm, structure, currentScope, symbolTable, initialValue); > } > > static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); >@@ -81,8 +79,6 @@ public: > > private: > bool symbolTableGet(PropertyName, PropertySlot&); >- bool symbolTableGet(PropertyName, PropertyDescriptor&); >- bool symbolTableGet(PropertyName, PropertySlot&, bool& slotIsWriteable); > bool symbolTablePut(ExecState*, PropertyName, JSValue, bool shouldThrow); > bool symbolTablePutWithAttributes(VM&, PropertyName, JSValue, unsigned attributes); > }; >Index: Source/JavaScriptCore/runtime/JSScope.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/JSScope.cpp (revision 186653) >+++ Source/JavaScriptCore/runtime/JSScope.cpp (working copy) >@@ -170,14 +170,14 @@ JSValue JSScope::resolve(ExecState* exec > } > } > >-ResolveOp JSScope::abstractResolve(ExecState* exec, bool hasTopActivation, JSScope* scope, const Identifier& ident, GetOrPut getOrPut, ResolveType unlinkedType) >+ResolveOp JSScope::abstractResolve(ExecState* exec, size_t depthOffset, JSScope* scope, const Identifier& ident, GetOrPut getOrPut, ResolveType unlinkedType) > { > ResolveOp op(Dynamic, 0, 0, 0, 0, 0); > if (unlinkedType == Dynamic) > return op; > >- size_t depth = hasTopActivation ? 1 : 0; > bool needsVarInjectionChecks = JSC::needsVarInjectionChecks(unlinkedType); >+ size_t depth = depthOffset; > for (; scope; scope = scope->next()) { > if (abstractAccess(exec, scope, ident, getOrPut, depth, needsVarInjectionChecks, op)) > break; >@@ -187,6 +187,28 @@ ResolveOp JSScope::abstractResolve(ExecS > return op; > } > >+void JSScope::collectVariablesUnderTDZ(JSScope* scope, VariableEnvironment& result) >+{ >+ for (; scope; scope = scope->next()) { >+ if (!scope->isLexicalScope()) >+ continue; >+ SymbolTable* symbolTable = jsCast<JSLexicalEnvironment*>(scope)->symbolTable(); >+ ASSERT(symbolTable->correspondsToLexicalScope()); >+ ConcurrentJITLocker locker(symbolTable->m_lock); >+ for (auto end = symbolTable->end(locker), iter = symbolTable->begin(locker); iter != end; ++iter) >+ result.add(iter->key); >+ } >+} >+ >+bool JSScope::isLexicalScope() >+{ >+ JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<JSLexicalEnvironment*>(this); >+ if (!lexicalEnvironment) // Global object does not hold any lexical variables so we can ignore it. >+ return false; >+ >+ return lexicalEnvironment->symbolTable()->correspondsToLexicalScope(); >+} >+ > const char* resolveModeName(ResolveMode mode) > { > static const char* const names[] = { >Index: Source/JavaScriptCore/runtime/JSScope.h >=================================================================== >--- Source/JavaScriptCore/runtime/JSScope.h (revision 186653) >+++ Source/JavaScriptCore/runtime/JSScope.h (working copy) >@@ -27,6 +27,7 @@ > #define JSScope_h > > #include "JSObject.h" >+#include "VariableEnvironment.h" > > namespace JSC { > >@@ -157,10 +158,14 @@ public: > static JSObject* objectAtScope(JSScope*); > > static JSValue resolve(ExecState*, JSScope*, const Identifier&); >- static ResolveOp abstractResolve(ExecState*, bool hasTopActivation, JSScope*, const Identifier&, GetOrPut, ResolveType); >+ static ResolveOp abstractResolve(ExecState*, size_t depthOffset, JSScope*, const Identifier&, GetOrPut, ResolveType); >+ >+ static void collectVariablesUnderTDZ(JSScope*, VariableEnvironment& result); > > static void visitChildren(JSCell*, SlotVisitor&); > >+ bool isLexicalScope(); >+ > ScopeChainIterator begin(); > ScopeChainIterator end(); > JSScope* next(); >Index: Source/JavaScriptCore/runtime/PropertySlot.h >=================================================================== >--- Source/JavaScriptCore/runtime/PropertySlot.h (revision 186653) >+++ Source/JavaScriptCore/runtime/PropertySlot.h (working copy) >@@ -125,7 +125,6 @@ public: > > void setValue(JSObject* slotBase, unsigned attributes, JSValue value) > { >- ASSERT(value); > m_data.value = JSValue::encode(value); > m_attributes = attributes; > >Index: Source/JavaScriptCore/runtime/SymbolTable.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/SymbolTable.cpp (revision 186653) >+++ Source/JavaScriptCore/runtime/SymbolTable.cpp (working copy) >@@ -84,6 +84,7 @@ SymbolTableEntry::FatEntry* SymbolTableE > SymbolTable::SymbolTable(VM& vm) > : JSCell(vm, vm.symbolTableStructure.get()) > , m_usesNonStrictEval(false) >+ , m_correspondsToLexicalScope(false) > { > } > >@@ -141,6 +142,7 @@ SymbolTable* SymbolTable::cloneScopePart > SymbolTable* result = SymbolTable::create(vm); > > result->m_usesNonStrictEval = m_usesNonStrictEval; >+ result->m_correspondsToLexicalScope = m_correspondsToLexicalScope; > > for (auto iter = m_map.begin(), end = m_map.end(); iter != end; ++iter) { > if (!iter->value.varOffset().isScope()) >Index: Source/JavaScriptCore/runtime/SymbolTable.h >=================================================================== >--- Source/JavaScriptCore/runtime/SymbolTable.h (revision 186653) >+++ Source/JavaScriptCore/runtime/SymbolTable.h (working copy) >@@ -648,6 +648,8 @@ public: > > bool usesNonStrictEval() { return m_usesNonStrictEval; } > void setUsesNonStrictEval(bool usesNonStrictEval) { m_usesNonStrictEval = usesNonStrictEval; } >+ ALWAYS_INLINE bool correspondsToLexicalScope() { return m_correspondsToLexicalScope; } >+ void setDoesCorrespondToLexicalScope() { m_correspondsToLexicalScope = true; } > > SymbolTable* cloneScopePart(VM&); > >@@ -675,7 +677,8 @@ private: > }; > std::unique_ptr<TypeProfilingRareData> m_typeProfilingRareData; > >- bool m_usesNonStrictEval; >+ bool m_usesNonStrictEval : 1; >+ bool m_correspondsToLexicalScope : 1; > > WriteBarrier<ScopedArgumentsTable> m_arguments; > WriteBarrier<InferredValue> m_singletonScope; >Index: Source/JavaScriptCore/runtime/WriteBarrier.h >=================================================================== >--- Source/JavaScriptCore/runtime/WriteBarrier.h (revision 186653) >+++ Source/JavaScriptCore/runtime/WriteBarrier.h (working copy) >@@ -145,6 +145,7 @@ public: > } > void clear() { m_value = JSValue::encode(JSValue()); } > void setUndefined() { m_value = JSValue::encode(jsUndefined()); } >+ void setStartingValue(JSValue value) { m_value = JSValue::encode(value); } > bool isNumber() const { return get().isNumber(); } > bool isObject() const { return get().isObject(); } > bool isNull() const { return get().isNull(); } >Index: Source/JavaScriptCore/tests/stress/activation-sink-default-value-tdz-error.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/activation-sink-default-value-tdz-error.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/activation-sink-default-value-tdz-error.js (working copy) >@@ -0,0 +1,44 @@ >+"use strict"; >+ >+var n = 10000000; >+ >+function shouldThrowTDZ(func) { >+ var hasThrown = false; >+ try { >+ func(); >+ } catch(e) { >+ if (e.name.indexOf("ReferenceError") !== -1) >+ hasThrown = true; >+ } >+ if (!hasThrown) >+ throw new Error("Did not throw TDZ error"); >+} >+noInline(shouldThrowTDZ); >+ >+function bar(f) { f(10); } >+ >+function foo(b) { >+ let result = 0; >+ var set = function (x) { result = x; } >+ var cap = function() { return tdzPerpetrator; } >+ if (b) { >+ bar(set); >+ return tdzPerpetrator; >+ } >+ let tdzPerpetrator; >+ return result; >+} >+ >+noInline(bar); >+noInline(foo); >+ >+for (var i = 0; i < n; i++) { >+ var bool = !(i % 100); >+ if (bool) >+ shouldThrowTDZ(function() { foo(bool); }); >+ else { >+ var result = foo(bool); >+ if (result != 0) >+ throw "Error: bad result: " + result; >+ } >+} >Index: Source/JavaScriptCore/tests/stress/activation-sink-osrexit-default-value-tdz-error.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/activation-sink-osrexit-default-value-tdz-error.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/activation-sink-osrexit-default-value-tdz-error.js (working copy) >@@ -0,0 +1,46 @@ >+"use strict"; >+ >+var n = 10000000; >+ >+function shouldThrowTDZ(func) { >+ var hasThrown = false; >+ try { >+ func(); >+ } catch(e) { >+ if (e.name.indexOf("ReferenceError") !== -1) >+ hasThrown = true; >+ } >+ if (!hasThrown) >+ throw new Error("Did not throw TDZ error"); >+} >+ >+function bar(f) { } >+ >+function foo(b) { >+ let result = 0; >+ var set = function (x) { result = x; return tdzPerpetrator; } >+ if (b) { >+ OSRExit(); >+ if (b) { >+ bar(set); >+ return tdzPerpetrator; >+ } >+ } >+ let tdzPerpetrator; >+ return result; >+} >+ >+noInline(bar); >+noInline(foo); >+noInline(shouldThrowTDZ); >+ >+for (var i = 0; i < n; i++) { >+ var bool = !(i % 100); >+ if (bool) >+ shouldThrowTDZ(function() { foo(bool); }); >+ else { >+ var result = foo(bool); >+ if (result != 0) >+ throw "Error: bad result: " + result; >+ } >+} >Index: Source/JavaScriptCore/tests/stress/lexical-let-and-with-statement.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/lexical-let-and-with-statement.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/lexical-let-and-with-statement.js (working copy) >@@ -0,0 +1,78 @@ >+function truth() { >+ return true; >+} >+noInline(truth); >+ >+function assert(cond) { >+ if (!cond) >+ throw new Error("broke assertion"); >+} >+ >+noInline(assert); >+ >+;(function() { >+ function foo() { >+ let x = 40; >+ with ({x : 100}) { >+ assert(x === 100); >+ } >+ with ({y : 100}) { >+ assert(x === 40); >+ } >+ } >+ noInline(foo); >+ >+ function bar() { >+ let x = 40; >+ function capX() { return x; } >+ with ({x : 100}) { >+ if (truth()) { >+ let x = 50; >+ let capX = function() { return x; } >+ assert(x === 50); >+ assert(capX() === x); >+ } >+ assert(x === 100); >+ assert(capX() === 40); >+ } >+ with ({y : 100}) { >+ if (truth()) { >+ let x = 50; >+ let capX = function() { return x; } >+ assert(x === 50); >+ assert(capX() === x); >+ } >+ assert(x === 40); >+ assert(capX() === 40); >+ } >+ } >+ noInline(bar); >+ >+ function baz() { >+ let x = 40; >+ function capX() { return x; } >+ with ({x : 100}) { >+ if (truth()) { >+ let x = 50; >+ assert(x === 50); >+ } >+ assert(x === 100); >+ assert(capX() === 40); >+ } >+ with ({y : 100}) { >+ if (truth()) { >+ let x = 50; >+ assert(x === 50); >+ } >+ assert(x === 40); >+ assert(capX() === 40); >+ } >+ } >+ noInline(baz); >+ >+ for (let i = 0; i < 1000; i++) { >+ foo(); >+ bar(); >+ baz(); >+ } >+})(); >Index: Source/JavaScriptCore/tests/stress/lexical-let-exception-handling.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/lexical-let-exception-handling.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/lexical-let-exception-handling.js (working copy) >@@ -0,0 +1,200 @@ >+"use strict"; >+ >+function truth() { >+ return true; >+} >+noInline(truth); >+ >+function assert(cond) { >+ if (!cond) >+ throw new Error("broke assertion"); >+} >+noInline(assert); >+ >+;(function () { >+ >+function foo() { >+ let x = 20; >+ let y = "y"; >+ try { >+ assert(x === 20); >+ assert(y === "y"); >+ throw "error"; >+ } catch(e) { >+ assert(x === 20); >+ } finally { >+ assert(x === 20); >+ assert(y === "y"); >+ } >+ >+ for (let i = 0; i < 1; i++) { >+ let numFinally = 0; >+ try { >+ let a = 40; >+ let capA = function() { return a; } >+ assert(capA() === 40); >+ try { >+ let b = 41; >+ let capB = function() { return b; } >+ assert(capB() === 41); >+ assert(capA() === 40); >+ try { >+ return 20; >+ } catch(e){ >+ } finally { >+ let c = 42; >+ let capC = function() { return c; } >+ assert(capC() === 42); >+ assert(capB() === 41); >+ assert(capA() === 40); >+ if (i === 0) { >+ numFinally++; >+ } >+ return 22; >+ } >+ } catch(e) { >+ } finally { >+ if (i === 0) { >+ numFinally++; >+ } >+ return 23; >+ } >+ } catch(e) { >+ } finally { >+ if (i === 0) { >+ numFinally++; >+ } >+ assert(numFinally === 3); >+ return 24; >+ } >+ } >+} >+ >+for (var i = 0; i < 1000; i++) { >+ assert(foo() === 24); >+} >+ >+})(); >+ >+ >+;(function () { >+function foo() { >+ for (let i = 0; i < 1; i++) { >+ let numFinally = 0; >+ let numErrors = 0; >+ try { >+ let a = 40; >+ let capA = function() { return a; } >+ assert(capA() === 40); >+ try { >+ let b = 41; >+ let capB = function() { return b; } >+ assert(capB() === 41); >+ assert(capA() === 40); >+ try { >+ throw "e"; >+ } catch(e) { >+ assert(i === 0); >+ assert(capB() === 41); >+ assert(capA() === 40); >+ numErrors++; >+ throw e; >+ } finally { >+ let c = 42; >+ let capC = function() { return c; } >+ let local = "local"; >+ assert(local === "local"); >+ assert(capC() === 42); >+ assert(capB() === 41); >+ assert(capA() === 40); >+ if (i === 0) { >+ numFinally++; >+ } >+ } >+ } catch(e) { >+ assert(i === 0); >+ assert(capA() === 40); >+ numErrors++; >+ let local = "local"; >+ assert(local === "local"); >+ } finally { >+ assert(capA() === 40); >+ if (i === 0) { >+ numFinally++; >+ } >+ let local = "local"; >+ assert(local === "local"); >+ return 23; >+ } >+ } catch(e) { >+ //assert(i === 0); >+ } finally { >+ if (i === 0) { >+ numFinally++; >+ } >+ >+ assert(numFinally === 3); >+ assert(numErrors === 2); >+ return 24; >+ } >+ } >+} >+ >+for (var i = 0; i < 1000; i++) { >+ assert(foo() === 24); >+} >+ >+})(); >+ >+ >+var d = 100; >+;(function (){ >+function foo() { >+ assert(d === 100); >+ for (let i = 0; i < 1; i++) { >+ let numFinally = 0; >+ let numErrors = 0; >+ let c = 44; >+ assert(d === 100); >+ try { >+ let d = 45; >+ if (truth()) { >+ let a = 20; >+ let capA = function() { return a; } >+ assert(capA() === 20); >+ if (truth()) { >+ let b = 21; >+ let e = 48; >+ let capB = function() { return b; } >+ assert(capB() === 21); >+ assert(d === 45); >+ try { >+ throw "e"; >+ } catch(e) { >+ assert(capA() === 20); >+ assert(a === 20); >+ numErrors++; >+ } finally { >+ assert(capA() === 20); >+ assert(e === 48); >+ numFinally++; >+ return 30; >+ } >+ } >+ } >+ } finally { >+ assert(c === 44); >+ assert(d === 100); >+ numFinally++; >+ assert(numFinally === 2); >+ assert(numErrors === 1); >+ return 40; >+ } >+ } >+} >+ >+for (var i = 0; i < 1000; i++) { >+ assert(foo() === 40); >+} >+ >+})(); >Index: Source/JavaScriptCore/tests/stress/lexical-let-global-not-captured-variables.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/lexical-let-global-not-captured-variables.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/lexical-let-global-not-captured-variables.js (working copy) >@@ -0,0 +1,31 @@ >+"use strict"; >+ >+function truth() { >+ return true; >+} >+noInline(truth); >+ >+function assert(cond) { >+ if (!cond) >+ throw new Error("broke assertion"); >+} >+noInline(assert); >+ >+ >+function foo(y) { >+ return y; >+} >+ >+let x = 40; >+assert(x === 40); >+ >+for (var i = 0; i < 1000; i++) { >+ if (truth()) { >+ let y = 20; >+ let capY = function() { return y; } >+ assert(x === 40); >+ assert(capY() === 20); >+ assert(foo(i) === i); >+ } >+} >+assert(foo("hello") === "hello"); >Index: Source/JavaScriptCore/tests/stress/lexical-let-loop-semantics.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/lexical-let-loop-semantics.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/lexical-let-loop-semantics.js (working copy) >@@ -0,0 +1,378 @@ >+"use strict"; >+ >+function truth() { >+ return true; >+} >+noInline(truth); >+ >+function assert(cond) { >+ if (!cond) >+ throw new Error("broke assertion"); >+} >+noInline(assert); >+ >+function shouldThrowTDZ(func) { >+ var hasThrown = false; >+ try { >+ func(); >+ } catch(e) { >+ if (e.name.indexOf("ReferenceError") !== -1) >+ hasThrown = true; >+ } >+ assert(hasThrown); >+} >+noInline(shouldThrowTDZ); >+ >+ >+;(function () { >+ var arr = []; >+ >+ for (let i = 0; i < 10; i++) { >+ arr.push(function () { return i; }) >+ } >+ >+ for (let j = 0; j < arr.length; j++) { >+ assert(arr[j]() === j); >+ } >+ >+ let f = "fff"; >+ let counter = 0; >+ for (let f of arr) { >+ assert(f() === counter++); >+ } >+ assert(f === "fff"); >+ >+ let numLoops = 0; >+ for (let f of arr) { >+ numLoops++; >+ let f = function () { return "f"; } >+ assert(f() === "f"); >+ } >+ assert(numLoops === arr.length); >+ assert(f === "fff"); >+ >+})(); >+ >+ >+;(function() { >+ function foo() { >+ var obj = {hello:1, world:2}; >+ obj["bar"] = 3; >+ let p = 20; >+ assert(p === 20); >+ for (let p in obj) { >+ assert(p === "hello" || p === "world" || p === "bar"); >+ } >+ assert(p === 20); >+ } >+ >+ function bar() { >+ var obj = {hello:1, world:2}; >+ obj["bar"] = 3; >+ let props = []; >+ let p = 20; >+ for (let p in obj) { >+ props.push(function foo() { return p; }); >+ let outerP = p; >+ if (truth()) { >+ let p = 100; >+ assert(p === 100); >+ assert(p !== outerP); >+ assert(outerP === "hello" || outerP === "world" || outerP === "bar"); >+ } >+ assert(p === outerP); >+ } >+ assert(p === 20); >+ >+ let seenProps = {}; >+ for (let f of props) { >+ let p = f(); >+ assert(p === "hello" || p === "world" || p === "bar"); >+ seenProps[p] = true; >+ } >+ assert(seenProps["hello"] === true); >+ assert(seenProps["world"] === true); >+ assert(seenProps["bar"] === true); >+ assert(p === 20); >+ } >+ >+ for (var i = 0; i < 1000; i++) { >+ foo(); >+ bar(); >+ } >+})(); >+ >+ >+;(function() { >+ function foo() { >+ let counter = 0; >+ for (let idx = 0; idx < 20; idx++) { >+ assert(idx === counter++); >+ continue; >+ } >+ shouldThrowTDZ(function() { return idx; }); >+ } >+ for (var i = 0; i < 1000; i++) { >+ foo(); >+ } >+})(); >+ >+ >+;(function() { >+ function foo() { >+ for (let idx = 0; !truth(); idx++) { } >+ shouldThrowTDZ(function() { return idx; }); // Just plain old reference error here. >+ } >+ function bar() { >+ for (let j = 0; j < 20; j++) { >+ if (j === 1) >+ break; >+ else >+ continue; >+ } >+ shouldThrowTDZ(function() { return j; }); >+ } >+ for (var i = 0; i < 1000; i++) { >+ foo(); >+ bar(); >+ } >+})(); >+ >+ >+;(function() { >+ function foo() { >+ var obj = {hello:1, world:2}; >+ let p = 20; >+ var arr = [] >+ for (let p in obj) { >+ arr.push(function capP() { return p; }); >+ assert(p === "hello" || p === "world"); >+ } >+ assert(arr[0]() === "hello" || arr[0]() === "world"); >+ assert(arr[1]() === "hello" || arr[1]() === "world"); >+ assert(arr[1]() !== arr[0]()); >+ assert(p === 20); >+ } >+ >+ function bar() { >+ var obj = {a:1, b:2, c:3, d:4, e:4}; >+ obj["f"] = 5; >+ let funcs = []; >+ for (let p in obj) { >+ funcs.push(function capP() { return p; }); >+ } >+ let counter = 0; >+ for (let p in obj) { >+ assert(funcs[counter]() === p); >+ counter++; >+ } >+ } >+ >+ for (var i = 0; i < 1000; i++) { >+ foo(); >+ bar(); >+ } >+})(); >+ >+ >+;(function() { >+ function foo() { >+ let arr = [0, 1, 2, 3, 4, 5]; >+ let funcs = []; >+ for (let x of arr) { >+ funcs.push(function() { return x; }); >+ } >+ for (let i = 0; i < arr.length; ++i) { >+ assert(funcs[i]() === i); >+ } >+ } >+ >+ for (var i = 0; i < 1000; i++) { >+ foo(); >+ } >+})(); >+ >+ >+;(function() { >+ function foo() { >+ let thing = {}; >+ for (let thing = thing; !thing; ) {} >+ } >+ for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ } >+})(); >+ >+ >+;(function() { >+ function foo() { >+ let thing = {}; >+ for (let thing = eval("thing"); !truth(); ) {} >+ } >+ >+ for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ } >+})(); >+ >+ >+;(function() { >+ function foo() { >+ let thing = {}; >+ for (let thing in thing) {} >+ } >+ function bar() { >+ let thing = {hello: "world"} >+ for (let thing in thing) {} >+ } >+ function baz() { >+ let thing = {}; >+ for (let thing in eval("thing")) {} >+ } >+ function bag() { >+ let thing = {hello: "world"} >+ for (let thing in eval("thing")) {} >+ } >+ for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+ shouldThrowTDZ(baz); >+ shouldThrowTDZ(bag); >+ } >+})(); >+ >+ >+;(function() { >+ function foo() { >+ let thing = ["hello"]; >+ for (let thing in thing) {} >+ } >+ function bar() { >+ let thing = []; >+ for (let thing in thing) {} >+ } >+ function baz() { >+ let thing = {hello: "world"}; >+ for (let thing in thing) {} >+ } >+ function bag() { >+ let empty = {}; >+ for (let thing in empty) {} >+ return thing; >+ } >+ function hat() { >+ let notEmpty = {foo: "bar"}; >+ for (let thing in notEmpty) { >+ break; >+ } >+ return thing; >+ } >+ function cap() { >+ let notEmpty = {foo: "bar"}; >+ for (let thing in notEmpty) { >+ continue; >+ } >+ return thing; >+ } >+ for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+ shouldThrowTDZ(baz); >+ shouldThrowTDZ(bag); >+ shouldThrowTDZ(hat); >+ shouldThrowTDZ(cap); >+ } >+})(); >+ >+ >+;(function() { >+ function foo() { >+ let thing = ["hello"]; >+ for (let thing of thing) {} >+ } >+ function bar() { >+ let thing = []; >+ for (let thing of thing) {} >+ } >+ function baz() { >+ let thing = ["world"] >+ for (let thing of thing) {} >+ } >+ function bag() { >+ let empty = []; >+ for (let thing of empty) {} >+ return thing; >+ } >+ function hat() { >+ let notEmpty = ["hello", "world"]; >+ for (let thing of notEmpty) { >+ break; >+ } >+ return thing; >+ } >+ function tap() { >+ let notEmpty = [10, 20]; >+ for (let thing of notEmpty) { } >+ return thing; >+ } >+ function cap() { >+ let notEmpty = [10, 20]; >+ for (let thing of notEmpty) { >+ continue; >+ } >+ return thing; >+ } >+ function pap() { >+ let notEmpty = [10, 20]; >+ for (let thing of notEmpty) { >+ } >+ return thing; >+ } >+ for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+ shouldThrowTDZ(baz); >+ shouldThrowTDZ(bag); >+ shouldThrowTDZ(hat); >+ shouldThrowTDZ(tap); >+ shouldThrowTDZ(cap); >+ shouldThrowTDZ(pap); >+ } >+})(); >+ >+ >+;(function() { >+ function foo() { >+ let x = 0; >+ let arr = []; >+ for (let x of (x=2, obj)) { x; } >+ } >+ >+ function bar() { >+ let x = 0; >+ let obj = {}; >+ for (let x in (x=2, obj)) { x; } >+ } >+ >+ for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+ } >+})(); >+ >+ >+;(function() { >+ let factorial = null; >+ function test() { >+ for (let factorial = function(x){ return x > 1 ? x * factorial(x - 1) : 1; }; true; ) { return factorial(5); } >+ } >+ assert(test() === 120); >+})(); >+ >+;(function() { >+ function test() { >+ for (let factorial = function(x){ return x > 1 ? x * factorial(x - 1) : 1; }; true; ) { return factorial(5); } >+ } >+ assert(test() === 120); >+})(); >+ >Index: Source/JavaScriptCore/tests/stress/lexical-let-not-strict-mode.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/lexical-let-not-strict-mode.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/lexical-let-not-strict-mode.js (working copy) >@@ -0,0 +1,128 @@ >+function truth() { >+ return true; >+} >+noInline(truth); >+ >+function assert(cond) { >+ if (!cond) >+ throw new Error("broke assertion"); >+} >+noInline(assert); >+ >+function shouldThrowTDZ(func) { >+ var hasThrown = false; >+ try { >+ func(); >+ } catch(e) { >+ if (e.name.indexOf("ReferenceError") !== -1) >+ hasThrown = true; >+ } >+ assert(hasThrown); >+} >+noInline(shouldThrowTDZ); >+ >+;(function() { >+ >+function foo() { >+ delete x; >+ let x; >+} >+function bar() { >+ delete x; >+ let x; >+ function capX() { return x; } >+} >+ >+for (var i = 0; i < 10000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+} >+ >+})(); >+ >+ >+;(function() { >+ >+function foo() { >+ var hadError = false; >+ try { >+ x; >+ } catch(e) { >+ hadError = true; >+ } >+ assert(hadError); >+ >+ if (truth()) { >+ // This eval is enterpreted as follows: >+ // eval("var x; x = 20"); >+ // We first assign undefined to the "var x". >+ // Then, we interperet an assignment expression >+ // into the resolved variable x. x resolves to the lexical "let x;" >+ // Look at ECMA section 13.3.2.4 of the ES6 spec: >+ // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-variable-statement-runtime-semantics-evaluation >+ // And also look at section 8.3.1 ResolveBinding: >+ // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-resolvebinding >+ let x = 40; >+ eval("var x = 20;"); >+ assert(x === 20); >+ } >+ assert(x === undefined); >+} >+ >+function bar() { >+ var hadError = false; >+ try { >+ x; >+ } catch(e) { >+ hadError = true; >+ } >+ assert(hadError); >+ >+ if (truth()) { >+ let x = 40; >+ function capX() { return x; } >+ eval("var x = 20;"); >+ assert(x === 20); >+ } >+ assert(x === undefined); >+} >+ >+function baz() { >+ if (truth()) { >+ let x = 40; >+ eval("let x = 20; assert(x === 20);"); >+ assert(x === 40); >+ } >+ if (truth()) { >+ let x = 40; >+ function capX() { return x; } >+ eval("let x = 20; assert(x === 20);"); >+ assert(x === 40); >+ } >+} >+ >+function baz() { >+ // Test eval() caching. >+ let x = 20; >+ let evalString = "x;"; >+ >+ assert(eval(evalString) === 20); >+ if (truth()) { >+ let y = 60; >+ assert(eval(evalString) === 20); >+ assert(y === 60); >+ if (truth()) { >+ let y = 50, z = 70, x = 40; >+ assert(eval(evalString) === 40); >+ assert(y === 50 && z === 70); >+ } >+ } >+} >+ >+for (var i = 0; i < 10000; i++) { >+ foo(); >+ bar(); >+ baz(); >+} >+ >+})(); >Index: Source/JavaScriptCore/tests/stress/lexical-let-semantics.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/lexical-let-semantics.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/lexical-let-semantics.js (working copy) >@@ -0,0 +1,411 @@ >+"use strict"; >+function truth() { >+ return true; >+} >+noInline(truth); >+ >+function assert(cond) { >+ if (!cond) >+ throw new Error("broke assertion"); >+} >+noInline(assert); >+ >+ >+// ========== tests below =========== >+ >+ >+let globalLet = "helloWorld"; >+assert(globalLet === "helloWorld"); >+function captureGlobalLet() { return globalLet; } >+assert(globalLet === captureGlobalLet()); >+let globalFunction = function() { return 20; } >+assert(globalFunction() === 20); >+assert((function() { return globalFunction(); })() === 20); >+let globalNumber = 20; >+assert(globalNumber === 20); >+globalNumber++; >+assert(globalNumber === 21); >+globalNumber += 40; >+assert(globalNumber === 61); >+globalNumber = "hello"; >+assert(globalNumber === "hello"); >+ >+let globalNumberCaptured = 20; >+let retGlobalNumberCaptured = function() { return globalNumberCaptured; } >+let setGlobalNumberCaptured = function(x) { globalNumberCaptured = x; } >+assert(retGlobalNumberCaptured() === globalNumberCaptured); >+globalNumberCaptured++; >+assert(retGlobalNumberCaptured() === globalNumberCaptured); >+assert(globalNumberCaptured === 21); >+setGlobalNumberCaptured(100); >+assert(retGlobalNumberCaptured() === globalNumberCaptured); >+assert(globalNumberCaptured === 100); >+setGlobalNumberCaptured(retGlobalNumberCaptured); >+assert(retGlobalNumberCaptured() === retGlobalNumberCaptured); >+assert(globalNumberCaptured === retGlobalNumberCaptured); >+ >+var arrOfFuncs = []; >+for (var i = 0; i < 1000; i++) { >+ let globalLet = "inner"; >+ assert(globalLet === "inner"); >+ let inner = i; >+ arrOfFuncs.push(function() { return inner; }); >+} >+assert(globalLet === "helloWorld"); >+for (var i = 0; i < arrOfFuncs.length; i++) >+ assert(arrOfFuncs[i]() === i); >+ >+ >+var globalVar = 100; >+assert(globalVar === 100); >+;(function () { >+ assert(globalVar === 100); >+ if (truth()) { >+ let globalVar = 20; >+ assert(globalVar === 20); >+ } >+ assert(globalVar === 100); >+})(); >+ >+assert(globalVar === 100); >+;(function () { >+ let globalVar = 10; >+ assert(globalVar === 10); >+ if (truth()) { >+ let globalVar = 20; >+ assert(globalVar === 20); >+ } >+ assert(globalVar === 10); >+})(); >+assert(globalVar === 100); >+ >+ >+;(function() { >+function foo() { >+ let x = 20; >+ >+ if (truth()) { >+ let thingy = function() { >+ x = 200; >+ return x; >+ }; >+ noInline(thingy); >+ thingy(); >+ } >+ >+ return x; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ assert(foo() === 200); >+} >+})(); >+ >+ >+;(function() { >+var arr = []; >+function foo(i) { >+ var num = i; >+ >+ if (truth()) { >+ let num = i; >+ arr.push(function() { return num; }); >+ } >+ var oldFunc = arr[arr.length - 1]; >+ arr[arr.length - 1] = function() { return oldFunc() + num; } >+} >+ >+for (var i = 0; i < 1000; i++) { >+ foo(i); >+} >+ >+for (var i = 0; i < arr.length; i++) { >+ assert(arr[i]() === i + i); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ let x = 20; >+ let y = 40; >+ assert(x === 20); >+ assert(y === 40); >+ if (truth()) { >+ let x = 50; >+ let y = 60; >+ assert(x === 50); >+ assert(y === 60); >+ } >+ assert(x === 20); >+ assert(y === 40); >+} >+ >+for (var i = 0; i < 1000; i++) { >+ foo(); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ function captureX() { return x; } >+ let x = 20; >+ let y = 40; >+ assert(x === 20); >+ assert(y === 40); >+ if (truth()) { >+ let x = 50; >+ let y = 60; >+ assert(x === 50); >+ assert(y === 60); >+ } >+ assert(x === 20); >+ assert(y === 40); >+} >+ >+for (var i = 0; i < 1000; i++) { >+ foo(); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ let x = 20; >+ let y = 40; >+ function captureAll() { return x + y; } >+ noInline(captureAll); >+ assert(x === 20); >+ assert(y === 40); >+ assert(captureAll() === 60); >+ if (truth()) { >+ let x = 50; >+ assert(x + y === 90); >+ assert(captureAll() === 60); >+ } >+ assert(x === 20); >+ assert(y === 40); >+} >+ >+for (var i = 0; i < 1000; i++) { >+ foo(); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ var captureAll = function() { return x + y; } >+ let x = 20; >+ let {_y : y, z} = {_y : 40, z : 100}; >+ assert(x === 20); >+ assert(y === 40); >+ assert(z === 100); >+ assert(captureAll() === 60); >+ if (truth()) { >+ let x = 50; >+ assert(x + y === 90); >+ assert(y === 40); >+ assert(captureAll() === 60); >+ } >+ assert(x === 20); >+ assert(y === 40); >+} >+ >+for (var i = 0; i < 1000; i++) { >+ foo(); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ var captureAll = function() { return x + y; } >+ let x = 20; >+ let y = 40; >+ assert(x === 20); >+ assert(y === 40); >+ assert(captureAll() === 60); >+ if (truth()) { >+ let x = 50; >+ let secondCaptureAll = function() { return x + y; }; >+ assert(x + y === 90); >+ assert(secondCaptureAll() === 90); >+ } >+ assert(x === 20); >+ assert(y === 40); >+} >+ >+for (var i = 0; i < 1000; i++) { >+ foo(); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ let x, y, z; >+ assert(x === undefined); >+ assert(y === undefined); >+ assert(z === undefined); >+} >+function bar() { >+ let x, y, z; >+ if (truth()) { >+ let x = 20, y = 40; >+ assert(x === 20); >+ assert(y === 40); >+ } >+ function capture() { return x + z; } >+ assert(x === undefined); >+ assert(y === undefined); >+ assert(z === undefined); >+} >+ >+for (var i = 0; i < 1000; i++) { >+ foo(); >+ bar(); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ let x, y, z = "z", t = undefined; >+ assert(cap() === undefined); >+ assert(x === undefined); >+ assert(y === undefined); >+ assert(t === undefined); >+ assert(z === "z"); >+ >+ function cap() { return x; } >+} >+ >+for (var i = 0; i < 1000; i++) { >+ foo(); >+} >+})(); >+ >+;(function() { >+function foo() { >+ let {x: baz} = {x: 20}; >+ let {x: bar} = {x: 200}; >+ function cap() { return baz; } >+ assert(baz === 20); >+ assert(bar === 200); >+ assert(cap() === 20); >+ baz = 40; >+ assert(baz === 40); >+ assert(cap() === 40); >+} >+ >+for (var i = 0; i < 1000; i++) { >+ foo(); >+} >+ >+})(); >+ >+ >+ >+;(function() { >+function foo() { >+ let x = 20; >+ let y = 50; >+ assert(y === 50); >+ assert(eval("y = 25; let x = 40; x;") === 40); >+ assert(x === 20); >+ assert(y === 25); >+} >+ >+for (var i = 0; i < 1000; i++) { >+ foo(); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ let x = 20; >+ let y = 50; >+ assert(y === 50); >+ if (truth()) { >+ let y = 30; >+ assert(y === 30); >+ assert(eval("y = 25; let x = 40; x;") === 40); >+ assert(y === 25); >+ assert(x === 20); >+ if (truth()) { >+ let y = 100; >+ assert(y === 100); >+ x = 1; >+ } >+ assert(x === 1); >+ assert(y === 25); >+ } >+ assert(x === 1); >+ assert(y === 50); >+} >+ >+for (var i = 0; i < 1000; i++) { >+ foo(); >+} >+})(); >+ >+ >+;(function() { >+function foo(x) { >+ let y = 50; >+ let result = null; >+ switch(x) { >+ case 10: >+ let y = 40; >+ assert(y === 40); >+ case 20: >+ y += 1; >+ assert(y === 41); >+ result = y; >+ break; >+ default: >+ result = x; >+ break; >+ } >+ assert(y === 50); >+ return result; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ assert(foo(10) === 41); >+} >+ >+assert(foo("hello") === "hello"); >+})(); >+ >+ >+;(function() { >+function foo(x) { >+ let y = 50; >+ let result = null; >+ switch(x) { >+ case 10: >+ let y = 40; >+ assert(y === 40); >+ case 20: >+ y += 1; >+ assert(y === 41); >+ result = function() { return y; }; >+ break; >+ default: >+ result = x; >+ break; >+ } >+ assert(y === 50); >+ return result; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ assert(foo(10)() === 41); >+} >+ >+assert(foo("hello") === "hello"); >+})(); >Index: Source/JavaScriptCore/tests/stress/lexical-let-tdz.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/lexical-let-tdz.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/lexical-let-tdz.js (working copy) >@@ -0,0 +1,470 @@ >+"use strict"; >+ >+function truth() { >+ return true; >+} >+noInline(truth); >+ >+function assert(cond) { >+ if (!cond) >+ throw new Error("broke assertion"); >+} >+noInline(assert); >+ >+function shouldThrowTDZ(func) { >+ var hasThrown = false; >+ try { >+ func(); >+ } catch(e) { >+ if (e.name.indexOf("ReferenceError") !== -1) >+ hasThrown = true; >+ } >+ assert(hasThrown); >+} >+noInline(shouldThrowTDZ); >+ >+ >+;(function() { >+function foo() { >+ x; >+ let x = 20; >+} >+function bar() { >+ let x = x; >+} >+function baz() { >+ let {x: prop, y: prop2} = {x: prop}; >+} >+function jaz() { >+ let {x: prop, y: prop2} = {x: 20, y: prop}; >+} >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+ shouldThrowTDZ(baz); >+ shouldThrowTDZ(jaz); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ x; >+ let x = 20; >+ function captureX() { return x; } >+} >+function bar() { >+ captureX(); >+ let x = 20; >+ function captureX() { return x; } >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ if (truth()) { >+ let x = 20; >+ assert(x === 20); >+ } >+ x; >+ let x = 20; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+} >+})(); >+ >+ >+ >+;(function() { >+function foo() { >+ if (truth()) { >+ let y = 20; >+ let captureY = function() { return y; } >+ assert(y === 20); >+ x; >+ } >+ let x = 20; >+ let y; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ if (truth()) { >+ let y = 20; >+ let x = 40; >+ let captureAll = function() { return x + y; } >+ //noInline(captureAll); >+ assert(y === 20); >+ assert(x === 40); >+ assert(captureAll() === 60); >+ } >+ tdz; >+ let tdz = 20; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ if (truth()) { >+ let y = 20; >+ let x = 40; >+ let captureAll = function() { return x + y + tdz; } >+ assert(y === 20); >+ assert(x === 40); >+ } >+ tdz; >+ let tdz = 20; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ x = 10; >+ let x = 20; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ x = 10; >+ let x = 20; >+ function cap() { return x; } >+} >+function bar() { >+ captureX(); >+ let x = 20; >+ function captureX() { return x; } >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ if (!truth()) { >+ y; >+ assert(false); >+ } >+ let y; >+ assert(y === undefined); >+ if (truth()) { >+ x; >+ } >+ let x; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ eval("x;"); >+ let x = 20; >+} >+function bar() { >+ function captureX() { return x; } >+ eval("captureX();"); >+ let x = 20; >+} >+function baz() { >+ function captureX() { return x; } >+ function other() { return captureX; } >+ other()(); >+ let x = 20; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+ shouldThrowTDZ(baz); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ let y; >+ eval("y; x;"); >+ let x = 20; >+} >+ >+for (var i = 0; i < 1; i++) { >+ shouldThrowTDZ(foo); >+} >+})(); >+ >+ >+;(function() { >+function foo() { >+ let x = 20; >+ let y = 40; >+ assert(eval("x;") === 20); >+ if (truth()) { >+ assert(eval("eval('y');") === 40); >+ eval("eval('x');"); >+ let x = 40; >+ } >+} >+ >+for (var i = 0; i < 1; i++) { >+ shouldThrowTDZ(foo); >+} >+})(); >+ >+ >+// FunctionResolveNode >+;(function() { >+function foo() { >+ function captureX() { return x; } >+ x(); >+ let x = function() { return 20; }; >+} >+ >+function bar() { >+ x(); >+ let x = function() { return 20; }; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+} >+})(); >+ >+ >+// TypeofResolveNode >+;(function() { >+function foo() { >+ function captureX() { return x; } >+ typeof x; >+ let x = function() { return 20; }; >+} >+ >+function bar() { >+ typeof x; >+ let x = function() { return 20; }; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+} >+})(); >+ >+ >+// ReadModifyResolveNode >+;(function() { >+function foo() { >+ function captureX() { return x; } >+ x++; >+ let x = 20; >+} >+ >+function bar() { >+ x--; >+ let x = 30; >+} >+ >+function baz() { >+ x *= 40; >+ let x = 30; >+} >+ >+function kaz() { >+ function captureX() { return x; } >+ x /= 20; >+ let x = 20; >+} >+ >+function haz() { >+ function captureX() { return x; } >+ --x; >+ let x = 20; >+} >+ >+function jaz() { >+ --x; >+ let x = 30; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(bar); >+ shouldThrowTDZ(baz); >+ shouldThrowTDZ(kaz); >+ shouldThrowTDZ(haz); >+ shouldThrowTDZ(jaz); >+} >+})(); >+ >+ >+;(function() { >+function foo(x) { >+ let y = 50; >+ let result = null; >+ switch(x) { >+ case 10: >+ let y = 40; >+ assert(y === 40); >+ case 20: >+ y += 1; >+ assert(y === 41); >+ result = y; >+ break; >+ default: >+ result = x; >+ break; >+ } >+ assert(y === 50); >+ return result; >+} >+function bar(x) { >+ let y = 50; >+ let result = null; >+ switch(x) { >+ case 10: >+ let y = 40; >+ assert(y === 40); >+ case 20: >+ let capY = function() { return y; } >+ y += 1; >+ assert(y === 41); >+ result = y; >+ break; >+ default: >+ result = x; >+ break; >+ } >+ assert(y === 50); >+ return result; >+} >+function baz(x) { >+ let y = 50; >+ let result = null; >+ switch(x) { >+ case 10: >+ let y = 40; >+ assert(y === 40); >+ case 20: >+ let inc = function() { y += 1; } >+ inc(); >+ assert(y === 41); >+ result = y; >+ break; >+ default: >+ result = x; >+ break; >+ } >+ assert(y === 50); >+ return result; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(function() { foo(20); }); >+ shouldThrowTDZ(function() { bar(20); }); >+ shouldThrowTDZ(function() { baz(20); }); >+} >+ >+assert(foo(10) === 41); >+assert(foo("hello") === "hello"); >+})(); >+ >+ >+;(function() { >+function foo() { >+ let y; >+ [y] = [40]; >+ assert(y === 40); >+ >+ [x] = [1]; >+ let x; >+} >+ >+function boo() { >+ let y; >+ [y] = [40]; >+ assert(y === 40); >+ >+ [x] = [1]; >+ let x; >+ function capX() { return x; } >+} >+ >+function bar() { >+ let x; >+ ({p: x} = {p: 40}); >+ assert(x === 40); >+ >+ ({a, p: y} = {a: 100, p: 40}); >+ let y; >+} >+ >+function zar() { >+ let x; >+ ({p: x} = {p: 40}); >+ assert(x === 40); >+ >+ ({a, p: y} = {a: 100, p: 40}); >+ let y; >+ function capY() { return y; } >+} >+ >+function baz() { >+ let x; >+ ({p: x} = {p: 40}); >+ assert(x === 40); >+ >+ ({a, p: {y}} = {a: 100, p: {p: {y: 40}}}); >+ let y; >+} >+ >+function jaz() { >+ ({y} = {}); >+ let y; >+} >+ >+for (var i = 0; i < 1000; i++) { >+ shouldThrowTDZ(foo); >+ shouldThrowTDZ(boo); >+ shouldThrowTDZ(bar); >+ shouldThrowTDZ(zar); >+ shouldThrowTDZ(baz); >+ shouldThrowTDZ(jaz); >+} >+})(); >Index: LayoutTests/js/keywords-and-reserved_words-expected.txt >=================================================================== >--- LayoutTests/js/keywords-and-reserved_words-expected.txt (revision 186653) >+++ LayoutTests/js/keywords-and-reserved_words-expected.txt (working copy) >@@ -60,13 +60,14 @@ PASS classifyIdentifier("import") is "ke > 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 >Index: LayoutTests/js/let-syntax-expected.txt >=================================================================== >--- LayoutTests/js/let-syntax-expected.txt (revision 0) >+++ LayoutTests/js/let-syntax-expected.txt (working copy) >@@ -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 >+ >Index: LayoutTests/js/let-syntax.html >=================================================================== >--- LayoutTests/js/let-syntax.html (revision 0) >+++ LayoutTests/js/let-syntax.html (working copy) >@@ -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: LayoutTests/js/reserved-words-strict-expected.txt >=================================================================== >--- LayoutTests/js/reserved-words-strict-expected.txt (revision 186653) >+++ LayoutTests/js/reserved-words-strict-expected.txt (working copy) >@@ -6,7 +6,6 @@ On success, you will see a series of "PA > 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: LayoutTests/js/statement-list-item-syntax-errors-expected.txt >=================================================================== >--- LayoutTests/js/statement-list-item-syntax-errors-expected.txt (revision 186653) >+++ LayoutTests/js/statement-list-item-syntax-errors-expected.txt (working copy) >@@ -99,6 +99,54 @@ PASS Had syntax error 'if (i = 0) const > 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: LayoutTests/js/dom/reserved-words-as-property-expected.txt >=================================================================== >--- LayoutTests/js/dom/reserved-words-as-property-expected.txt (revision 186653) >+++ LayoutTests/js/dom/reserved-words-as-property-expected.txt (working copy) >@@ -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: LayoutTests/js/script-tests/keywords-and-reserved_words.js >=================================================================== >--- LayoutTests/js/script-tests/keywords-and-reserved_words.js (revision 186653) >+++ LayoutTests/js/script-tests/keywords-and-reserved_words.js (working copy) >@@ -104,10 +104,15 @@ shouldBe('classifyIdentifier("super")', > // 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")'); >Index: LayoutTests/js/script-tests/let-syntax.js >=================================================================== >--- LayoutTests/js/script-tests/let-syntax.js (revision 0) >+++ LayoutTests/js/script-tests/let-syntax.js (working copy) >@@ -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: LayoutTests/js/script-tests/reserved-words-strict.js >=================================================================== >--- LayoutTests/js/script-tests/reserved-words-strict.js (revision 186653) >+++ LayoutTests/js/script-tests/reserved-words-strict.js (working copy) >@@ -16,7 +16,6 @@ function isReserved(word) > > var reservedWords = [ > "implements", >- "let", > "private", > "public", > "yield", >Index: LayoutTests/js/script-tests/statement-list-item-syntax-errors.js >=================================================================== >--- LayoutTests/js/script-tests/statement-list-item-syntax-errors.js (revision 186653) >+++ LayoutTests/js/script-tests/statement-list-item-syntax-errors.js (working copy) >@@ -43,13 +43,13 @@ function testSyntax(asBlock, body, throw > } > > 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);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 31813
:
256546
|
256608
|
256611
|
256761
|
256926
|
256930
|
256934
|
256945
|
256951
|
256953
|
257039
|
257040
|
257041
|
257051