Index: Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp =================================================================== --- Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp (revision 268822) +++ Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp (working copy) @@ -82,7 +82,7 @@ static UnlinkedFunctionCodeBlock* genera return result; } -UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM& vm, Structure* structure, const SourceCode& parentSource, FunctionMetadataNode* node, UnlinkedFunctionKind kind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional parentScopeTDZVariables, DerivedContextType derivedContextType, NeedsClassFieldInitializer needsClassFieldInitializer, bool isBuiltinDefaultClassConstructor) +UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM& vm, Structure* structure, const SourceCode& parentSource, FunctionMetadataNode* node, UnlinkedFunctionKind kind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional> parentScopeTDZVariables, DerivedContextType derivedContextType, NeedsClassFieldInitializer needsClassFieldInitializer, bool isBuiltinDefaultClassConstructor) : Base(vm, structure) , m_firstLineOffset(node->firstLine() - parentSource.firstLine().oneBasedInt()) , m_isInStrictContext(node->isInStrictContext()) Index: Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h =================================================================== --- Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h (revision 268822) +++ Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h (working copy) @@ -70,7 +70,7 @@ public: return &vm.unlinkedFunctionExecutableSpace.space; } - static UnlinkedFunctionExecutable* create(VM& vm, const SourceCode& source, FunctionMetadataNode* node, UnlinkedFunctionKind unlinkedFunctionKind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional parentScopeTDZVariables, DerivedContextType derivedContextType, NeedsClassFieldInitializer needsClassFieldInitializer, bool isBuiltinDefaultClassConstructor = false) + static UnlinkedFunctionExecutable* create(VM& vm, const SourceCode& source, FunctionMetadataNode* node, UnlinkedFunctionKind unlinkedFunctionKind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, Optional> parentScopeTDZVariables, DerivedContextType derivedContextType, NeedsClassFieldInitializer needsClassFieldInitializer, bool isBuiltinDefaultClassConstructor = false) { UnlinkedFunctionExecutable* instance = new (NotNull, allocateCell(vm.heap)) UnlinkedFunctionExecutable(vm, vm.unlinkedFunctionExecutableStructure.get(), source, node, unlinkedFunctionKind, constructAbility, scriptMode, WTFMove(parentScopeTDZVariables), derivedContextType, needsClassFieldInitializer, isBuiltinDefaultClassConstructor); @@ -170,9 +170,9 @@ public: VariableEnvironment parentScopeTDZVariables() const { - if (!m_rareData || !m_rareData->m_parentScopeTDZVariables) + if (!m_rareData || m_rareData->m_parentScopeTDZVariables.isEmpty()) return VariableEnvironment(); - return m_rareData->m_parentScopeTDZVariables.environment().toVariableEnvironment(); + return variableEnvironmentFromCompactHandleStack(m_rareData->m_parentScopeTDZVariables); } bool isArrowFunction() const { return isArrowFunctionParseMode(parseMode()); } @@ -208,7 +208,7 @@ public: SourceCode m_classSource; String m_sourceURLDirective; String m_sourceMappingURLDirective; - CompactVariableMap::Handle m_parentScopeTDZVariables; + Vector m_parentScopeTDZVariables; Vector m_instanceFieldLocations; }; @@ -229,7 +229,7 @@ public: } private: - UnlinkedFunctionExecutable(VM&, Structure*, const SourceCode&, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, Optional, JSC::DerivedContextType, JSC::NeedsClassFieldInitializer, bool isBuiltinDefaultClassConstructor); + UnlinkedFunctionExecutable(VM&, Structure*, const SourceCode&, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, Optional>, JSC::DerivedContextType, JSC::NeedsClassFieldInitializer, bool isBuiltinDefaultClassConstructor); UnlinkedFunctionExecutable(Decoder&, const CachedFunctionExecutable&); static void visitChildren(JSCell*, SlotVisitor&); Index: Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp =================================================================== --- Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp (revision 268822) +++ Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp (working copy) @@ -48,6 +48,7 @@ #include "Options.h" #include "PrivateFieldPutKind.h" #include "StrongInlines.h" +#include "SuperSampler.h" #include "UnlinkedCodeBlock.h" #include "UnlinkedEvalCodeBlock.h" #include "UnlinkedFunctionCodeBlock.h" @@ -300,7 +301,6 @@ BytecodeGenerator::BytecodeGenerator(VM& , m_isBuiltinFunction(false) , m_usesNonStrictEval(false) , m_inTailPosition(false) - , m_hasCachedVariablesUnderTDZ(false) , m_needsToUpdateArrowFunctionContext(programNode->usesArrowFunction() || programNode->usesEval()) , m_ecmaMode(ecmaMode) { @@ -355,7 +355,6 @@ BytecodeGenerator::BytecodeGenerator(VM& // // Note that we intentionally enable tail call for naked constructors since it does not have special code for "return". , m_inTailPosition(Options::useTailCalls() && !isConstructor() && constructorKind() == ConstructorKind::None && ecmaMode.isStrict()) - , m_hasCachedVariablesUnderTDZ(false) , m_needsToUpdateArrowFunctionContext(functionNode->usesArrowFunction() || functionNode->usesEval()) , m_ecmaMode(ecmaMode) , m_derivedContextType(codeBlock->derivedContextType()) @@ -852,7 +851,6 @@ BytecodeGenerator::BytecodeGenerator(VM& , m_isBuiltinFunction(false) , m_usesNonStrictEval(codeBlock->usesEval() && !ecmaMode.isStrict()) , m_inTailPosition(false) - , m_hasCachedVariablesUnderTDZ(false) , m_needsToUpdateArrowFunctionContext(evalNode->usesArrowFunction() || evalNode->usesEval()) , m_ecmaMode(ecmaMode) , m_derivedContextType(codeBlock->derivedContextType()) @@ -916,7 +914,6 @@ BytecodeGenerator::BytecodeGenerator(VM& , m_isBuiltinFunction(false) , m_usesNonStrictEval(false) , m_inTailPosition(false) - , m_hasCachedVariablesUnderTDZ(false) , m_needsToUpdateArrowFunctionContext(moduleProgramNode->usesArrowFunction() || moduleProgramNode->usesEval()) , m_ecmaMode(ecmaMode) { @@ -2170,7 +2167,7 @@ void BytecodeGenerator::popLexicalScopeI } m_TDZStack.removeLast(); - m_cachedVariablesUnderTDZ = { }; + m_cachedVariablesUnderTDZ.removeLast(); } void BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode* node, RegisterID* loopSymbolTable) @@ -2880,10 +2877,8 @@ void BytecodeGenerator::liftTDZCheckIfPo for (unsigned i = m_TDZStack.size(); i--;) { auto iter = m_TDZStack[i].find(identifier); if (iter != m_TDZStack[i].end()) { - if (iter->value == TDZNecessityLevel::Optimize) { - m_cachedVariablesUnderTDZ = { }; + if (iter->value == TDZNecessityLevel::Optimize) iter->value = TDZNecessityLevel::NotNeeded; - } break; } } @@ -2908,59 +2903,57 @@ void BytecodeGenerator::pushTDZVariables map.add(entry.key, entry.value.isFunction() ? TDZNecessityLevel::NotNeeded : level); m_TDZStack.append(WTFMove(map)); - m_cachedVariablesUnderTDZ = { }; + m_cachedVariablesUnderTDZ.append({ }); } -Optional BytecodeGenerator::getVariablesUnderTDZ() +Optional BytecodeGenerator::getVariablesUnderTDZ() { - if (m_cachedVariablesUnderTDZ) { - if (!m_hasCachedVariablesUnderTDZ) { - ASSERT(m_cachedVariablesUnderTDZ.environment().toVariableEnvironment().isEmpty()); - return WTF::nullopt; - } + SuperSamplerScope superSamplerScope(false); + + auto assertCacheIsCoherent = [&] { +#if ASSERT_ENABLED + for (size_t i = 0; i < m_cachedVariablesUnderTDZ.size(); ++i) + ASSERT(!!m_cachedVariablesUnderTDZ[i]); +#endif + }; + + RELEASE_ASSERT(m_TDZStack.size() == m_cachedVariablesUnderTDZ.size()); + + if (m_cachedVariablesUnderTDZ.isEmpty()) + return WTF::nullopt; + + if (m_cachedVariablesUnderTDZ.last()) { + assertCacheIsCoherent(); return m_cachedVariablesUnderTDZ; } - // We keep track of variablesThatDontNeedTDZ in this algorithm to prevent - // reporting that "x" is under TDZ if this function is called at "...". - // - // { - // { - // let x; - // ... - // } - // let x; - // } - SmallPtrSet variablesThatDontNeedTDZ; - VariableEnvironment environment; - for (unsigned i = m_TDZStack.size(); i--; ) { + for (size_t i = m_TDZStack.size(); i--;) { + if (m_cachedVariablesUnderTDZ[i]) + break; + auto& map = m_TDZStack[i]; - for (auto& entry : map) { - if (entry.value != TDZNecessityLevel::NotNeeded) { - if (!variablesThatDontNeedTDZ.contains(entry.key.get())) - environment.add(entry.key.get()); - } else - variablesThatDontNeedTDZ.add(entry.key.get()); + VariableEnvironment environment; + for (auto& entry : map) { + if (entry.value != TDZNecessityLevel::NotNeeded) + environment.add(entry.key.get()); } + m_cachedVariablesUnderTDZ[i] = m_vm.m_compactVariableMap->get(environment); } - m_cachedVariablesUnderTDZ = m_vm.m_compactVariableMap->get(environment); - m_hasCachedVariablesUnderTDZ = !environment.isEmpty(); - if (!m_hasCachedVariablesUnderTDZ) - return WTF::nullopt; - + assertCacheIsCoherent(); return m_cachedVariablesUnderTDZ; } void BytecodeGenerator::preserveTDZStack(BytecodeGenerator::PreservedTDZStack& preservedStack) { preservedStack.m_preservedTDZStack = m_TDZStack; + preservedStack.m_cachedTDZStack = m_cachedVariablesUnderTDZ; } void BytecodeGenerator::restoreTDZStack(const BytecodeGenerator::PreservedTDZStack& preservedStack) { m_TDZStack = preservedStack.m_preservedTDZStack; - m_cachedVariablesUnderTDZ = { }; + m_cachedVariablesUnderTDZ = preservedStack.m_cachedTDZStack; } RegisterID* BytecodeGenerator::emitNewObject(RegisterID* dst) @@ -3147,7 +3140,7 @@ RegisterID* BytecodeGenerator::emitNewIn superBinding = SuperBinding::Needed; } - Optional variablesUnderTDZ = getVariablesUnderTDZ(); + auto variablesUnderTDZ = getVariablesUnderTDZ(); SourceParseMode parseMode = SourceParseMode::InstanceFieldInitializerMode; ConstructAbility constructAbility = ConstructAbility::CannotConstruct; Index: Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h =================================================================== --- Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h (revision 268822) +++ Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h (working copy) @@ -1185,7 +1185,7 @@ namespace JSC { newDerivedContextType = DerivedContextType::DerivedMethodContext; } - Optional optionalVariablesUnderTDZ = getVariablesUnderTDZ(); + auto optionalVariablesUnderTDZ = getVariablesUnderTDZ(); // FIXME: These flags, ParserModes and propagation to XXXCodeBlocks should be reorganized. // https://bugs.webkit.org/show_bug.cgi?id=151547 @@ -1197,7 +1197,9 @@ namespace JSC { return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), WTFMove(optionalVariablesUnderTDZ), newDerivedContextType, needsClassFieldInitializer); } - Optional getVariablesUnderTDZ(); + using CachedTDZStack = Vector; + + Optional getVariablesUnderTDZ(); RegisterID* emitConstructVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); template @@ -1231,6 +1233,7 @@ namespace JSC { class PreservedTDZStack { private: Vector m_preservedTDZStack; + CachedTDZStack m_cachedTDZStack; friend class BytecodeGenerator; }; @@ -1263,6 +1266,7 @@ namespace JSC { Vector m_lexicalScopeStack; Vector m_TDZStack; + CachedTDZStack m_cachedVariablesUnderTDZ; Optional m_varScopeLexicalScopeStackIndex; void pushTDZVariables(const VariableEnvironment&, TDZCheckOptimization, TDZRequirement); @@ -1344,13 +1348,10 @@ namespace JSC { bool m_isBuiltinFunction { false }; bool m_usesNonStrictEval { false }; bool m_inTailPosition { false }; - bool m_hasCachedVariablesUnderTDZ { false }; bool m_needsToUpdateArrowFunctionContext : 1; ECMAMode m_ecmaMode; DerivedContextType m_derivedContextType { DerivedContextType::None }; - CompactVariableMap::Handle m_cachedVariablesUnderTDZ; - struct CatchEntry { TryData* tryData; VirtualRegister exceptionRegister; Index: Source/JavaScriptCore/parser/VariableEnvironment.cpp =================================================================== --- Source/JavaScriptCore/parser/VariableEnvironment.cpp (revision 268822) +++ Source/JavaScriptCore/parser/VariableEnvironment.cpp (working copy) @@ -61,6 +61,19 @@ void VariableEnvironment::markAllVariabl value.setIsCaptured(); } +void VariableEnvironment::markAllVariablesAsCapturedWithMapValuesAlreadyMarkedAsCaptured() +{ + if (m_isEverythingCaptured) + return; + + m_isEverythingCaptured = true; // For fast queries. +#if ASSERT_ENABLED + // Every entry must be marked as captured for when we iterate through m_map and entry.isCaptured() is called. + for (auto& value : m_map.values()) + ASSERT(value.isCaptured()); +#endif +} + bool VariableEnvironment::hasCapturedVariables() const { if (m_isEverythingCaptured) @@ -217,4 +230,28 @@ CompactVariableMap::Handle::Handle(Compa { } +VariableEnvironment variableEnvironmentFromCompactHandleStack(const Vector& handles) +{ + VariableEnvironment result; + bool everythingIsCaptured = true; + for (size_t i = handles.size(); i--; ) { + const CompactVariableEnvironment& compactEnvironment = handles[i].environment(); + for (size_t j = 0; j < compactEnvironment.variables().size(); ++j) { + auto addResult = result.add(compactEnvironment.variables()[j]); + if (!addResult.isNewEntry) + continue; + auto entry = compactEnvironment.variableMetadata()[j]; + if (compactEnvironment.isEverythingCaptured()) + entry.setIsCaptured(); + everythingIsCaptured &= entry.isCaptured(); + addResult.iterator->value = entry; + } + } + + if (everythingIsCaptured) + result.markAllVariablesAsCapturedWithMapValuesAlreadyMarkedAsCaptured(); + + return result; +} + } // namespace JSC Index: Source/JavaScriptCore/parser/VariableEnvironment.h =================================================================== --- Source/JavaScriptCore/parser/VariableEnvironment.h (revision 268822) +++ Source/JavaScriptCore/parser/VariableEnvironment.h (working copy) @@ -154,6 +154,7 @@ public: void markVariableAsCapturedIfDefined(const RefPtr& identifier); void markVariableAsCaptured(const RefPtr& identifier); void markAllVariablesAsCaptured(); + void markAllVariablesAsCapturedWithMapValuesAlreadyMarkedAsCaptured(); bool hasCapturedVariables() const; bool captures(UniquedStringImpl* identifier) const; void markVariableAsImported(const RefPtr& identifier); @@ -275,6 +276,10 @@ public: bool operator==(const CompactVariableEnvironment&) const; unsigned hash() const { return m_hash; } + const Vector>& variables() const { return m_variables; } + const Vector& variableMetadata() const { return m_variableMetadata; } + bool isEverythingCaptured() const { return m_isEverythingCaptured; } + private: CompactVariableEnvironment() = default; @@ -404,4 +409,6 @@ private: HashMap m_map; }; +VariableEnvironment variableEnvironmentFromCompactHandleStack(const Vector&); + } // namespace JSC Index: Source/JavaScriptCore/runtime/CachedTypes.cpp =================================================================== --- Source/JavaScriptCore/runtime/CachedTypes.cpp (revision 268822) +++ Source/JavaScriptCore/runtime/CachedTypes.cpp (working copy) @@ -1744,7 +1744,7 @@ public: void encode(Encoder& encoder, const UnlinkedFunctionExecutable::RareData& rareData) { m_classSource.encode(encoder, rareData.m_classSource); - m_parentScopeTDZVariables.encode(encoder, rareData.m_parentScopeTDZVariables); + m_parentScopeTDZVariables.encode(encoder, rareData.m_parentScopeTDZVariables); } UnlinkedFunctionExecutable::RareData* decode(Decoder& decoder) const @@ -1758,7 +1758,7 @@ public: private: CachedSourceCodeWithoutProvider m_classSource; - CachedCompactVariableMapHandle m_parentScopeTDZVariables; + CachedVector m_parentScopeTDZVariables; }; class CachedFunctionExecutable : public CachedObject {