WebKit Bugzilla
Attachment 359575 Details for
Bug 193603
: [JSC] Invalidate old scope operations using global lexical binding epoch
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-193603-20190118185825.patch (text/plain), 51.70 KB, created by
Yusuke Suzuki
on 2019-01-18 18:58:25 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Yusuke Suzuki
Created:
2019-01-18 18:58:25 PST
Size:
51.70 KB
patch
obsolete
>Subversion Revision: 240156 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 840d862438c2dc7245a6c38c196b94a6f24ab327..d2add1511eac1a15153db36606deac1b79fd755c 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,80 @@ >+2019-01-18 Yusuke Suzuki <yusukesuzuki@slowstart.org> >+ >+ [JSC] Invalidate old scope operations using global lexical binding epoch >+ https://bugs.webkit.org/show_bug.cgi?id=193603 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Even if the global lexical binding does not shadow the global property at that time, we need to clear the cached information in >+ scope related operations since we may have a global property previously. Consider the following example, >+ >+ foo = 0; >+ function get() { return foo; } >+ print(get()); // 0 >+ print(get()); // 0 >+ delete globalThis.foo; >+ $.evalScript(`const foo = 42;`); >+ print(get()); // Should be 42, but it returns 0 if the cached information in get() is not cleared. >+ >+ To invalidate the cache easily, we introduce global lexical binding epoch. It is bumped every time we introduce a new lexical binding >+ into JSGlobalLexicalEnvironment, since that name could shadow the global property name previously. In op_resolve_scope, we first check >+ the epoch stored in the metadata, and go to slow path if it is not equal to the current epoch. Our slow path code convert the scope >+ operation to the appropriate one even if the resolve type is not UnresolvedProperty type. After updating the resolve type of the bytecode, >+ we update the cached epoch to the current one, so that we can use the cached information as long as we stay in the same epoch. >+ >+ In op_get_from_scope and op_put_to_scope, we do not use this epoch since Structure check can do the same thing instead. If op_resolve_type >+ is updated by the epoch, and if it starts returning JSGlobalLexicalEnvironment instead JSGlobalObject, obviously the structure check fails. >+ And in the slow path, we update op_get_from_scope and op_put_to_scope appropriately. >+ >+ So, the metadata for scope related bytecodes are eventually updated to the appropriate one. In DFG and FTL, we use the watchpoint based approach. >+ In DFG and FTL, we concurrently attempt to get the watchpoint for the lexical binding and look into it by using `isStillValid()` to avoid >+ infinite compile-and-fail loop. >+ >+ When the global lexical binding epoch overflows we iterate all the live CodeBlock and update the op_resolve_scope's epoch. Even if the shadowing >+ happens, it is OK if we bump the epoch, since op_resolve_scope will return JSGlobalLexicalEnvironment instead of JSGlobalObject, and following >+ structure check in op_put_to_scope and op_get_from_scope fail. We do not need to update op_get_from_scope and op_put_to_scope because of the same >+ reason. >+ >+ * bytecode/BytecodeList.rb: >+ * bytecode/CodeBlock.cpp: >+ (JSC::CodeBlock::notifyLexicalBindingUpdate): >+ (JSC::CodeBlock::notifyLexicalBindingShadowing): Deleted. >+ * bytecode/CodeBlock.h: >+ * dfg/DFGByteCodeParser.cpp: >+ (JSC::DFG::ByteCodeParser::parseBlock): >+ * dfg/DFGDesiredGlobalProperties.cpp: >+ (JSC::DFG::DesiredGlobalProperties::isStillValidOnMainThread): >+ * dfg/DFGDesiredGlobalProperties.h: >+ * dfg/DFGGraph.cpp: >+ (JSC::DFG::Graph::watchGlobalProperty): >+ * dfg/DFGGraph.h: >+ * dfg/DFGPlan.cpp: >+ (JSC::DFG::Plan::isStillValidOnMainThread): >+ * jit/JITPropertyAccess.cpp: >+ (JSC::JIT::emit_op_resolve_scope): >+ * jit/JITPropertyAccess32_64.cpp: >+ (JSC::JIT::emit_op_resolve_scope): >+ * llint/LowLevelInterpreter32_64.asm: >+ * llint/LowLevelInterpreter64.asm: >+ * runtime/CommonSlowPaths.cpp: >+ (JSC::SLOW_PATH_DECL): >+ * runtime/CommonSlowPaths.h: >+ (JSC::CommonSlowPaths::tryCachePutToScopeGlobal): >+ (JSC::CommonSlowPaths::tryCacheGetFromScopeGlobal): >+ * runtime/JSGlobalObject.cpp: >+ (JSC::JSGlobalObject::bumpGlobalLexicalBindingEpoch): >+ (JSC::JSGlobalObject::getReferencedPropertyWatchpointSetConcurrently): >+ (JSC::JSGlobalObject::ensureReferencedPropertyWatchpointSet): >+ (JSC::JSGlobalObject::notifyLexicalBindingShadowing): Deleted. >+ (JSC::JSGlobalObject::getReferencedPropertyWatchpointSet): Deleted. >+ * runtime/JSGlobalObject.h: >+ (JSC::JSGlobalObject::globalLexicalBindingEpoch const): >+ (JSC::JSGlobalObject::globalLexicalBindingEpochOffset): >+ (JSC::JSGlobalObject::addressOfGlobalLexicalBindingEpoch): >+ * runtime/Options.h: >+ * runtime/ProgramExecutable.cpp: >+ (JSC::ProgramExecutable::initializeGlobalProperties): >+ > 2019-01-17 Mark Lam <mark.lam@apple.com> > > Audit bytecode fields and ensure that LLInt instructions for accessing them are appropriate. >diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.rb b/Source/JavaScriptCore/bytecode/BytecodeList.rb >index bb5f1261600e5fdb194eae98c08b54647d7d3b7d..115bd3b065d65031773cc1f3126d33fd5575423c 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeList.rb >+++ b/Source/JavaScriptCore/bytecode/BytecodeList.rb >@@ -833,8 +833,11 @@ > }, > metadata: { > resolveType: ResolveType, # offset 4 >- localScopeDepth: unsigned, # offset 5 >- _: { # offset 6 >+ _0: { # offset 5 >+ localScopeDepth: unsigned, >+ globalLexicalBindingEpoch: unsigned, >+ }, >+ _1: { # offset 6 > # written during linking > lexicalEnvironment: WriteBarrierBase[JSCell], # lexicalEnvironment && type == ModuleVar > symbolTable: WriteBarrierBase[SymbolTable], # lexicalEnvironment && type != ModuleVar >diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >index 0c6345686fa1e53d53c49338597e7c1e3555e21c..2b28bf8d1d97770aa465446c5ea4380995ec2f36 100644 >--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp >+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >@@ -2668,18 +2668,25 @@ void CodeBlock::tallyFrequentExitSites() > } > #endif // ENABLE(DFG_JIT) > >-void CodeBlock::notifyLexicalBindingShadowing(VM& vm, const IdentifierSet& set) >+void CodeBlock::notifyLexicalBindingUpdate(VM& vm) > { > // FIXME: Currently, module code do not query to JSGlobalLexicalEnvironment. So this case should be removed once it is fixed. > // https://bugs.webkit.org/show_bug.cgi?id=193347 > if (scriptMode() == JSParserScriptMode::Module) > return; > JSGlobalObject* globalObject = m_globalObject.get(); >+ JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(globalObject->globalScope()); >+ SymbolTable* symbolTable = globalLexicalEnvironment->symbolTable(); > > auto scope = DECLARE_THROW_SCOPE(vm); > > ConcurrentJSLocker locker(m_lock); > >+ auto isShadowed = [&] (UniquedStringImpl* uid) { >+ ConcurrentJSLocker locker(symbolTable->m_lock); >+ return symbolTable->contains(locker, uid); >+ }; >+ > for (const auto& instruction : *m_instructions) { > OpcodeID opcodeID = instruction->opcodeID(); > switch (opcodeID) { >@@ -2689,72 +2696,13 @@ void CodeBlock::notifyLexicalBindingShadowing(VM& vm, const IdentifierSet& set) > ResolveType originalResolveType = metadata.m_resolveType; > if (originalResolveType == GlobalProperty || originalResolveType == GlobalPropertyWithVarInjectionChecks) { > const Identifier& ident = identifier(bytecode.m_var); >- if (set.contains(ident.impl())) { >- // We pass JSGlobalLexicalScope as a start point of the scope chain. >- // It should immediately find the lexical binding because that's the reason why we perform this rewriting now. >- ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), bytecode.m_localScopeDepth, globalObject->globalScope(), ident, Get, bytecode.m_resolveType, InitializationMode::NotInitialization); >- scope.releaseAssertNoException(); >- ASSERT(op.type == GlobalLexicalVarWithVarInjectionChecks || op.type == GlobalLexicalVar); >- metadata.m_resolveType = needsVarInjectionChecks(originalResolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar; >- metadata.m_localScopeDepth = 0; >- ASSERT(!op.lexicalEnvironment); >- JSScope* constantScope = JSScope::constantScopeForCodeBlock(metadata.m_resolveType, this); >- ASSERT(constantScope == globalObject->globalScope()); >- metadata.m_constantScope.set(vm, this, constantScope); >- dataLogLnIf(CodeBlockInternal::verbose, "Rewrite op_resolve_scope from ", originalResolveType, " to ", metadata.m_resolveType); >- } >- } >- break; >- } >- >- case op_get_from_scope: { >- auto bytecode = instruction->as<OpGetFromScope>(); >- auto& metadata = bytecode.metadata(this); >- ResolveType originalResolveType = metadata.m_getPutInfo.resolveType(); >- if (originalResolveType == GlobalProperty || originalResolveType == GlobalPropertyWithVarInjectionChecks) { >- const Identifier& ident = identifier(bytecode.m_var); >- if (set.contains(ident.impl())) { >- // We pass JSGlobalLexicalScope as a start point of the scope chain. >- // It should immediately find the lexical binding because that's the reason why we perform this rewriting now. >- ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), bytecode.m_localScopeDepth, globalObject->globalScope(), ident, Get, bytecode.m_getPutInfo.resolveType(), InitializationMode::NotInitialization); >- scope.releaseAssertNoException(); >- ASSERT(op.type == GlobalLexicalVarWithVarInjectionChecks || op.type == GlobalLexicalVar); >- metadata.m_getPutInfo = GetPutInfo(bytecode.m_getPutInfo.resolveMode(), needsVarInjectionChecks(originalResolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar, bytecode.m_getPutInfo.initializationMode()); >- metadata.m_watchpointSet = op.watchpointSet; >- metadata.m_operand = op.operand; >- dataLogLnIf(CodeBlockInternal::verbose, "Rewrite op_get_from_scope from ", originalResolveType, " to ", metadata.m_getPutInfo.resolveType()); >- } >+ if (isShadowed(ident.impl())) >+ metadata.m_globalLexicalBindingEpoch = 0; >+ else >+ metadata.m_globalLexicalBindingEpoch = globalObject->globalLexicalBindingEpoch(); > } > break; > } >- >- case op_put_to_scope: { >- auto bytecode = instruction->as<OpPutToScope>(); >- auto& metadata = bytecode.metadata(this); >- ResolveType originalResolveType = metadata.m_getPutInfo.resolveType(); >- if (originalResolveType == GlobalProperty || originalResolveType == GlobalPropertyWithVarInjectionChecks) { >- const Identifier& ident = identifier(bytecode.m_var); >- if (set.contains(ident.impl())) { >- // We pass JSGlobalLexicalScope as a start point of the scope chain. >- // It should immediately find the lexical binding because that's the reason why we perform this rewriting now. >- ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), bytecode.m_symbolTableOrScopeDepth, globalObject->globalScope(), ident, Put, bytecode.m_getPutInfo.resolveType(), bytecode.m_getPutInfo.initializationMode()); >- scope.releaseAssertNoException(); >- ASSERT(op.type == GlobalLexicalVarWithVarInjectionChecks || op.type == GlobalLexicalVar || op.type == Dynamic); >- >- ResolveType resolveType = op.type; >- metadata.m_watchpointSet = nullptr; >- if (resolveType == GlobalLexicalVarWithVarInjectionChecks || resolveType == GlobalLexicalVar) { >- resolveType = needsVarInjectionChecks(originalResolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar; >- metadata.m_watchpointSet = op.watchpointSet; >- } >- metadata.m_getPutInfo = GetPutInfo(bytecode.m_getPutInfo.resolveMode(), resolveType, bytecode.m_getPutInfo.initializationMode()); >- metadata.m_operand = op.operand; >- dataLogLnIf(CodeBlockInternal::verbose, "Rewrite op_put_to_scope from ", originalResolveType, " to ", metadata.m_getPutInfo.resolveType()); >- } >- } >- break; >- } >- > default: > break; > } >diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.h b/Source/JavaScriptCore/bytecode/CodeBlock.h >index a6926fdf97eb4e6c6fcd0eb276f805319ba0bfe4..8a2654c5e482dbfb51b873e820dc7f5f63d6a2d0 100644 >--- a/Source/JavaScriptCore/bytecode/CodeBlock.h >+++ b/Source/JavaScriptCore/bytecode/CodeBlock.h >@@ -195,7 +195,7 @@ class CodeBlock : public JSCell { > void visitChildren(SlotVisitor&); > void finalizeUnconditionally(VM&); > >- void notifyLexicalBindingShadowing(VM&, const IdentifierSet&); >+ void notifyLexicalBindingUpdate(VM&); > > void dumpSource(); > void dumpSource(PrintStream&); >diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >index 4e18df703d3d216686f5859824f2ac6cb97fe00e..9d1614dea3042b7e2bc0608f1e78d4c651921298 100644 >--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >@@ -6167,9 +6167,10 @@ void ByteCodeParser::parseBlock(unsigned limit) > // https://bugs.webkit.org/show_bug.cgi?id=193347 > if (m_inlineStackTop->m_codeBlock->scriptMode() != JSParserScriptMode::Module) { > if (resolveType == GlobalProperty || resolveType == GlobalPropertyWithVarInjectionChecks) { >- unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[bytecode.m_var]; > JSGlobalObject* globalObject = m_inlineStackTop->m_codeBlock->globalObject(); >- m_graph.globalProperties().addLazily(DesiredGlobalProperty(globalObject, identifierNumber)); >+ unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[bytecode.m_var]; >+ if (!m_graph.watchGlobalProperty(globalObject, identifierNumber)) >+ addToGraph(ForceOSRExit); > } > } > >@@ -6281,8 +6282,10 @@ void ByteCodeParser::parseBlock(unsigned limit) > case GlobalPropertyWithVarInjectionChecks: { > // FIXME: Currently, module code do not query to JSGlobalLexicalEnvironment. So this case should be removed once it is fixed. > // https://bugs.webkit.org/show_bug.cgi?id=193347 >- if (m_inlineStackTop->m_codeBlock->scriptMode() != JSParserScriptMode::Module) >- m_graph.globalProperties().addLazily(DesiredGlobalProperty(globalObject, identifierNumber)); >+ if (m_inlineStackTop->m_codeBlock->scriptMode() != JSParserScriptMode::Module) { >+ if (!m_graph.watchGlobalProperty(globalObject, identifierNumber)) >+ addToGraph(ForceOSRExit); >+ } > > SpeculatedType prediction = getPrediction(); > >@@ -6456,8 +6459,10 @@ void ByteCodeParser::parseBlock(unsigned limit) > case GlobalPropertyWithVarInjectionChecks: { > // FIXME: Currently, module code do not query to JSGlobalLexicalEnvironment. So this case should be removed once it is fixed. > // https://bugs.webkit.org/show_bug.cgi?id=193347 >- if (m_inlineStackTop->m_codeBlock->scriptMode() != JSParserScriptMode::Module) >- m_graph.globalProperties().addLazily(DesiredGlobalProperty(globalObject, identifierNumber)); >+ if (m_inlineStackTop->m_codeBlock->scriptMode() != JSParserScriptMode::Module) { >+ if (!m_graph.watchGlobalProperty(globalObject, identifierNumber)) >+ addToGraph(ForceOSRExit); >+ } > > PutByIdStatus status; > if (uid) >diff --git a/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.cpp b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.cpp >index 8f7b9ced99b8f79090a720e9e4ebfedad6e9af03..94114165e39916be2c53b0cef0e4a15d4fc14124 100644 >--- a/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.cpp >+++ b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.cpp >@@ -36,16 +36,23 @@ > > namespace JSC { namespace DFG { > >-bool DesiredGlobalProperties::isStillValidOnMainThread(DesiredIdentifiers& identifiers) >+bool DesiredGlobalProperties::isStillValidOnMainThread(VM& vm, DesiredIdentifiers& identifiers) > { >+ bool isStillValid = true; > for (const auto& property : m_set) { > auto* uid = identifiers.at(property.identifierNumber()); >- if (auto* watchpointSet = property.globalObject()->getReferencedPropertyWatchpointSet(uid)) { >- if (!watchpointSet->isStillValid()) >- return false; >+ JSGlobalObject* globalObject = property.globalObject(); >+ { >+ SymbolTable* symbolTable = globalObject->globalLexicalEnvironment()->symbolTable(); >+ ConcurrentJSLocker locker(symbolTable->m_lock); >+ if (!symbolTable->contains(locker, uid)) >+ continue; > } >+ // Set invalidated WatchpointSet here to prevent further compile-and-fail loop. >+ property.globalObject()->ensureReferencedPropertyWatchpointSet(uid).fireAll(vm, "Lexical binding shadows the existing global properties"); >+ isStillValid = false; > } >- return true; >+ return isStillValid; > } > > void DesiredGlobalProperties::reallyAdd(CodeBlock* codeBlock, DesiredIdentifiers& identifiers, CommonData& common) >diff --git a/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.h b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.h >index e340a59651e5102044e38a46b9cdf9f54dac0928..69bd36da9bfbcce16eb5215ef9019f4d17145f7b 100644 >--- a/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.h >+++ b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.h >@@ -47,7 +47,7 @@ class DesiredGlobalProperties { > m_set.add(WTFMove(property)); > } > >- bool isStillValidOnMainThread(DesiredIdentifiers&); >+ bool isStillValidOnMainThread(VM&, DesiredIdentifiers&); > > void reallyAdd(CodeBlock*, DesiredIdentifiers&, CommonData&); > >diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp >index 83cc1d4b64eb4db9162a908cbdccc7e7e855c080..2a9ded1c9f7711236c7aec5860c12a5940a379d6 100644 >--- a/Source/JavaScriptCore/dfg/DFGGraph.cpp >+++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp >@@ -1058,6 +1058,19 @@ bool Graph::isSafeToLoad(JSObject* base, PropertyOffset offset) > return m_safeToLoad.contains(std::make_pair(base, offset)); > } > >+bool Graph::watchGlobalProperty(JSGlobalObject* globalObject, unsigned identifierNumber) >+{ >+ UniquedStringImpl* uid = identifiers()[identifierNumber]; >+ // If we already have a WatchpointSet, and it is already invalidated, it means that this scope operation must be changed from GlobalProperty to GlobalLexicalVar, >+ // but this is not done yet since we do not execute this op again. Just emitting ForceOSRExit to update the metadata when it reaches to this code. >+ if (auto* watchpoint = globalObject->getReferencedPropertyWatchpointSetConcurrently(uid)) { >+ if (!watchpoint->isStillValid()) >+ return false; >+ } >+ globalProperties().addLazily(DesiredGlobalProperty(globalObject, identifierNumber)); >+ return true; >+} >+ > FullBytecodeLiveness& Graph::livenessFor(CodeBlock* codeBlock) > { > HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>>::iterator iter = m_bytecodeLiveness.find(codeBlock); >diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h >index ee2e267e0ce5d6c2438edb3353ec53efb0c17fe8..151f5736c7ac7073f1dedee8df6eb33e008ef4eb 100644 >--- a/Source/JavaScriptCore/dfg/DFGGraph.h >+++ b/Source/JavaScriptCore/dfg/DFGGraph.h >@@ -793,6 +793,8 @@ class Graph : public virtual Scannable { > bool watchCondition(const ObjectPropertyCondition&); > bool watchConditions(const ObjectPropertyConditionSet&); > >+ bool watchGlobalProperty(JSGlobalObject*, unsigned identifierNumber); >+ > // Checks if it's known that loading from the given object at the given offset is fine. This is > // computed by tracking which conditions we track with watchCondition(). > bool isSafeToLoad(JSObject* base, PropertyOffset); >diff --git a/Source/JavaScriptCore/dfg/DFGPlan.cpp b/Source/JavaScriptCore/dfg/DFGPlan.cpp >index e77723f884d9f3560d8bfa2cf414ae00fdeb1bf5..c5e899ba156c7743b66c3d63ba2d95a83f231897 100644 >--- a/Source/JavaScriptCore/dfg/DFGPlan.cpp >+++ b/Source/JavaScriptCore/dfg/DFGPlan.cpp >@@ -573,7 +573,7 @@ void Plan::notifyReady() > > bool Plan::isStillValidOnMainThread() > { >- return m_globalProperties.isStillValidOnMainThread(m_identifiers); >+ return m_globalProperties.isStillValidOnMainThread(*m_vm, m_identifiers); > } > > CompilationResult Plan::finalizeWithoutNotifyingCallback() >diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp >index 92f37ec9097757ea703a0e7555ac227e57a0ede7..1682ee3800301fe5837eb714c69f24faed92bb82 100644 >--- a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp >+++ b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp >@@ -803,6 +803,8 @@ void JIT::emit_op_resolve_scope(const Instruction* currentInstruction) > JSScope** constantScopeSlot = metadata.m_constantScope.slot(); > emitVarInjectionCheck(needsVarInjectionChecks(resolveType)); > loadPtr(constantScopeSlot, regT0); >+ load32(&metadata.m_globalLexicalBindingEpoch, regT1); >+ addSlowCase(branch32(NotEqual, AbsoluteAddress(m_codeBlock->globalObject()->addressOfGlobalLexicalBindingEpoch()), regT1)); > emitPutVirtualRegister(dst); > break; > } >diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp >index 33d6ae460864376a0540b69d1564be5efac1369f..d0393624b0023aa8085cf1920fd1dd1968d03683 100644 >--- a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp >+++ b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp >@@ -806,6 +806,8 @@ void JIT::emit_op_resolve_scope(const Instruction* currentInstruction) > // Since these GlobalProperty can be changed to GlobalLexicalVar, we should load the value from metadata. > JSScope** constantScopeSlot = metadata.m_constantScope.slot(); > emitVarInjectionCheck(needsVarInjectionChecks(resolveType)); >+ load32(&metadata.m_globalLexicalBindingEpoch, regT1); >+ addSlowCase(branch32(NotEqual, AbsoluteAddress(m_codeBlock->globalObject()->addressOfGlobalLexicalBindingEpoch()), regT1)); > move(TrustedImm32(JSValue::CellTag), regT1); > loadPtr(constantScopeSlot, regT0); > emitStore(dst, regT1, regT0); >diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >index 6f7deabc7251a8579f7e995ad20477b76a30030f..c443b48b6ca57ec8f0a9cd2631ad61e6872242ff 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >@@ -2092,11 +2092,20 @@ end > > llintOpWithMetadata(op_resolve_scope, OpResolveScope, macro (size, get, dispatch, metadata, return) > >- macro getConstantScope() >- loadp OpResolveScope::Metadata::m_constantScope[t5], t0 >+ macro getConstantScope(dst) >+ loadp OpResolveScope::Metadata::m_constantScope[t5], dst >+ end >+ >+ macro returnConstantScope() >+ getConstantScope(t0) > return(CellTag, t0) > end > >+ macro globalLexicalBindingEpochCheck(slowPath, globalObject, scratch) >+ loadi OpResolveScope::Metadata::m_globalLexicalBindingEpoch[globalObject], scratch >+ bineq JSGlobalObject::m_globalLexicalBindingEpoch[globalObject], scratch, slowPath >+ end >+ > macro resolveScope() > loadi OpResolveScope::Metadata::m_localScopeDepth[t5], t2 > get(m_scope, t0) >@@ -2117,15 +2126,17 @@ llintOpWithMetadata(op_resolve_scope, OpResolveScope, macro (size, get, dispatch > > #rGlobalProperty: > bineq t0, GlobalProperty, .rGlobalVar >- getConstantScope() >+ getConstantScope(t0) >+ globalLexicalBindingEpochCheck(.rDynamic, t0, t2) >+ return(CellTag, t0) > > .rGlobalVar: > bineq t0, GlobalVar, .rGlobalLexicalVar >- getConstantScope() >+ returnConstantScope() > > .rGlobalLexicalVar: > bineq t0, GlobalLexicalVar, .rClosureVar >- getConstantScope() >+ returnConstantScope() > > .rClosureVar: > bineq t0, ClosureVar, .rModuleVar >@@ -2133,22 +2144,24 @@ llintOpWithMetadata(op_resolve_scope, OpResolveScope, macro (size, get, dispatch > > .rModuleVar: > bineq t0, ModuleVar, .rGlobalPropertyWithVarInjectionChecks >- getConstantScope() >+ returnConstantScope() > > .rGlobalPropertyWithVarInjectionChecks: > bineq t0, GlobalPropertyWithVarInjectionChecks, .rGlobalVarWithVarInjectionChecks > varInjectionCheck(.rDynamic) >- getConstantScope() >+ getConstantScope(t0) >+ globalLexicalBindingEpochCheck(.rDynamic, t0, t2) >+ return(CellTag, t0) > > .rGlobalVarWithVarInjectionChecks: > bineq t0, GlobalVarWithVarInjectionChecks, .rGlobalLexicalVarWithVarInjectionChecks > varInjectionCheck(.rDynamic) >- getConstantScope() >+ returnConstantScope() > > .rGlobalLexicalVarWithVarInjectionChecks: > bineq t0, GlobalLexicalVarWithVarInjectionChecks, .rClosureVarWithVarInjectionChecks > varInjectionCheck(.rDynamic) >- getConstantScope() >+ returnConstantScope() > > .rClosureVarWithVarInjectionChecks: > bineq t0, ClosureVarWithVarInjectionChecks, .rDynamic >diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >index 148c839507f5eb7e9bf561791bbb0c2e74b9524e..e32b60159eb9e2d4acd1f1ebf7aa86220d6250fb 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >@@ -2150,11 +2150,20 @@ end > llintOpWithMetadata(op_resolve_scope, OpResolveScope, macro (size, get, dispatch, metadata, return) > metadata(t5, t0) > >- macro getConstantScope() >- loadp OpResolveScope::Metadata::m_constantScope[t5], t0 >+ macro getConstantScope(dst) >+ loadp OpResolveScope::Metadata::m_constantScope[t5], dst >+ end >+ >+ macro returnConstantScope() >+ getConstantScope(t0) > return(t0) > end > >+ macro globalLexicalBindingEpochCheck(slowPath, globalObject, scratch) >+ loadi OpResolveScope::Metadata::m_globalLexicalBindingEpoch[globalObject], scratch >+ bineq JSGlobalObject::m_globalLexicalBindingEpoch[globalObject], scratch, slowPath >+ end >+ > macro resolveScope() > loadi OpResolveScope::Metadata::m_localScopeDepth[t5], t2 > get(m_scope, t0) >@@ -2174,15 +2183,17 @@ llintOpWithMetadata(op_resolve_scope, OpResolveScope, macro (size, get, dispatch > > #rGlobalProperty: > bineq t0, GlobalProperty, .rGlobalVar >- getConstantScope() >+ getConstantScope(t0) >+ globalLexicalBindingEpochCheck(.rDynamic, t0, t2) >+ return(t0) > > .rGlobalVar: > bineq t0, GlobalVar, .rGlobalLexicalVar >- getConstantScope() >+ returnConstantScope() > > .rGlobalLexicalVar: > bineq t0, GlobalLexicalVar, .rClosureVar >- getConstantScope() >+ returnConstantScope() > > .rClosureVar: > bineq t0, ClosureVar, .rModuleVar >@@ -2190,22 +2201,24 @@ llintOpWithMetadata(op_resolve_scope, OpResolveScope, macro (size, get, dispatch > > .rModuleVar: > bineq t0, ModuleVar, .rGlobalPropertyWithVarInjectionChecks >- getConstantScope() >+ returnConstantScope() > > .rGlobalPropertyWithVarInjectionChecks: > bineq t0, GlobalPropertyWithVarInjectionChecks, .rGlobalVarWithVarInjectionChecks > varInjectionCheck(.rDynamic, t2) >- getConstantScope() >+ getConstantScope(t0) >+ globalLexicalBindingEpochCheck(.rDynamic, t0, t2) >+ return(t0) > > .rGlobalVarWithVarInjectionChecks: > bineq t0, GlobalVarWithVarInjectionChecks, .rGlobalLexicalVarWithVarInjectionChecks > varInjectionCheck(.rDynamic, t2) >- getConstantScope() >+ returnConstantScope() > > .rGlobalLexicalVarWithVarInjectionChecks: > bineq t0, GlobalLexicalVarWithVarInjectionChecks, .rClosureVarWithVarInjectionChecks > varInjectionCheck(.rDynamic, t2) >- getConstantScope() >+ returnConstantScope() > > .rClosureVarWithVarInjectionChecks: > bineq t0, ClosureVarWithVarInjectionChecks, .rDynamic >@@ -2256,7 +2269,7 @@ llintOpWithMetadata(op_get_from_scope, OpGetFromScope, macro (size, get, dispatc > > #gGlobalProperty: > bineq t0, GlobalProperty, .gGlobalVar >- loadWithStructureCheck(OpGetFromScope, get, .gDynamic) >+ loadWithStructureCheck(OpGetFromScope, get, .gDynamic) # This structure check includes lexical binding epoch check since when the epoch is changed, scope will be changed too. > getProperty() > > .gGlobalVar: >@@ -2277,7 +2290,7 @@ llintOpWithMetadata(op_get_from_scope, OpGetFromScope, macro (size, get, dispatc > > .gGlobalPropertyWithVarInjectionChecks: > bineq t0, GlobalPropertyWithVarInjectionChecks, .gGlobalVarWithVarInjectionChecks >- loadWithStructureCheck(OpGetFromScope, get, .gDynamic) >+ loadWithStructureCheck(OpGetFromScope, get, .gDynamic) # This structure check includes lexical binding epoch check since when the epoch is changed, scope will be changed too. > getProperty() > > .gGlobalVarWithVarInjectionChecks: >@@ -2364,7 +2377,7 @@ llintOpWithMetadata(op_put_to_scope, OpPutToScope, macro (size, get, dispatch, m > > .pGlobalProperty: > bineq t0, GlobalProperty, .pGlobalVar >- loadWithStructureCheck(OpPutToScope, get, .pDynamic) >+ loadWithStructureCheck(OpPutToScope, get, .pDynamic) # This structure check includes lexical binding epoch check since when the epoch is changed, scope will be changed too. > putProperty() > writeBarrierOnOperands(size, get, m_scope, m_value) > dispatch() >@@ -2391,7 +2404,7 @@ llintOpWithMetadata(op_put_to_scope, OpPutToScope, macro (size, get, dispatch, m > > .pGlobalPropertyWithVarInjectionChecks: > bineq t0, GlobalPropertyWithVarInjectionChecks, .pGlobalVarWithVarInjectionChecks >- loadWithStructureCheck(OpPutToScope, get, .pDynamic) >+ loadWithStructureCheck(OpPutToScope, get, .pDynamic) # This structure check includes lexical binding epoch check since when the epoch is changed, scope will be changed too. > putProperty() > writeBarrierOnOperands(size, get, m_scope, m_value) > dispatch() >diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >index b880458ec877e5186074c09d1275836d747c3148..aaa94386744af6e696659a06f601497f8e5526f6 100644 >--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >@@ -1067,29 +1067,31 @@ SLOW_PATH_DECL(slow_path_resolve_scope) > // ModuleVar does not keep the scope register value alive in DFG. > ASSERT(resolveType != ModuleVar); > >- if (resolveType == UnresolvedProperty || resolveType == UnresolvedPropertyWithVarInjectionChecks) { >+ switch (resolveType) { >+ case GlobalProperty: >+ case GlobalPropertyWithVarInjectionChecks: // Global Lexical Binding Epoch is changed. Update op_resolve_scope. >+ case UnresolvedProperty: >+ case UnresolvedPropertyWithVarInjectionChecks: { > if (resolvedScope->isGlobalObject()) { > JSGlobalObject* globalObject = jsCast<JSGlobalObject*>(resolvedScope); > bool hasProperty = globalObject->hasProperty(exec, ident); > CHECK_EXCEPTION(); > if (hasProperty) { > ConcurrentJSLocker locker(exec->codeBlock()->m_lock); >- if (resolveType == UnresolvedProperty) >- metadata.m_resolveType = GlobalProperty; >- else >- metadata.m_resolveType = GlobalPropertyWithVarInjectionChecks; >- >+ metadata.m_resolveType = needsVarInjectionChecks(resolveType) ? GlobalPropertyWithVarInjectionChecks : GlobalProperty; > metadata.m_globalObject = globalObject; >+ metadata.m_globalLexicalBindingEpoch = globalObject->globalLexicalBindingEpoch(); > } > } else if (resolvedScope->isGlobalLexicalEnvironment()) { > JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(resolvedScope); > ConcurrentJSLocker locker(exec->codeBlock()->m_lock); >- if (resolveType == UnresolvedProperty) >- metadata.m_resolveType = GlobalLexicalVar; >- else >- metadata.m_resolveType = GlobalLexicalVarWithVarInjectionChecks; >+ metadata.m_resolveType = needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar; > metadata.m_globalLexicalEnvironment = globalLexicalEnvironment; > } >+ break; >+ } >+ default: >+ break; > } > > RETURN(resolvedScope); >diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.h b/Source/JavaScriptCore/runtime/CommonSlowPaths.h >index 130a0c82a2f7ef6ed873e0629579c3a33677acac..7eeaf704b43354396097b9eb4821177eb5a14959 100644 >--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.h >+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.h >@@ -124,28 +124,39 @@ inline void tryCachePutToScopeGlobal( > // Covers implicit globals. Since they don't exist until they first execute, we didn't know how to cache them at compile time. > auto& metadata = bytecode.metadata(exec); > ResolveType resolveType = metadata.m_getPutInfo.resolveType(); >- if (resolveType != GlobalProperty && resolveType != GlobalPropertyWithVarInjectionChecks >- && resolveType != UnresolvedProperty && resolveType != UnresolvedPropertyWithVarInjectionChecks) >- return; > >- if (resolveType == UnresolvedProperty || resolveType == UnresolvedPropertyWithVarInjectionChecks) { >+ switch (resolveType) { >+ case UnresolvedProperty: >+ case UnresolvedPropertyWithVarInjectionChecks: { > if (scope->isGlobalObject()) { >- ResolveType newResolveType = resolveType == UnresolvedProperty ? GlobalProperty : GlobalPropertyWithVarInjectionChecks; >- resolveType = newResolveType; >+ ResolveType newResolveType = needsVarInjectionChecks(resolveType) ? GlobalPropertyWithVarInjectionChecks : GlobalProperty; >+ resolveType = newResolveType; // Allow below caching mechanism to kick in. > ConcurrentJSLocker locker(codeBlock->m_lock); > metadata.m_getPutInfo = GetPutInfo(metadata.m_getPutInfo.resolveMode(), newResolveType, metadata.m_getPutInfo.initializationMode()); >- } else if (scope->isGlobalLexicalEnvironment()) { >+ break; >+ } >+ FALLTHROUGH; >+ } >+ case GlobalProperty: >+ case GlobalPropertyWithVarInjectionChecks: { >+ // Global Lexical Binding Epoch is changed. Update op_get_from_scope from GlobalProperty to GlobalLexicalVar. >+ if (scope->isGlobalLexicalEnvironment()) { > JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(scope); >- ResolveType newResolveType = resolveType == UnresolvedProperty ? GlobalLexicalVar : GlobalLexicalVarWithVarInjectionChecks; >+ ResolveType newResolveType = needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar; > metadata.m_getPutInfo = GetPutInfo(metadata.m_getPutInfo.resolveMode(), newResolveType, metadata.m_getPutInfo.initializationMode()); > SymbolTableEntry entry = globalLexicalEnvironment->symbolTable()->get(ident.impl()); > ASSERT(!entry.isNull()); > ConcurrentJSLocker locker(codeBlock->m_lock); > metadata.m_watchpointSet = entry.watchpointSet(); > metadata.m_operand = reinterpret_cast<uintptr_t>(globalLexicalEnvironment->variableAt(entry.scopeOffset()).slot()); >+ return; > } >+ break; > } >- >+ default: >+ return; >+ } >+ > if (resolveType == GlobalProperty || resolveType == GlobalPropertyWithVarInjectionChecks) { > VM& vm = exec->vm(); > JSGlobalObject* globalObject = codeBlock->globalObject(); >@@ -176,22 +187,36 @@ inline void tryCacheGetFromScopeGlobal( > auto& metadata = bytecode.metadata(exec); > ResolveType resolveType = metadata.m_getPutInfo.resolveType(); > >- if (resolveType == UnresolvedProperty || resolveType == UnresolvedPropertyWithVarInjectionChecks) { >+ switch (resolveType) { >+ case UnresolvedProperty: >+ case UnresolvedPropertyWithVarInjectionChecks: { > if (scope->isGlobalObject()) { >- ResolveType newResolveType = resolveType == UnresolvedProperty ? GlobalProperty : GlobalPropertyWithVarInjectionChecks; >+ ResolveType newResolveType = needsVarInjectionChecks(resolveType) ? GlobalPropertyWithVarInjectionChecks : GlobalProperty; > resolveType = newResolveType; // Allow below caching mechanism to kick in. > ConcurrentJSLocker locker(exec->codeBlock()->m_lock); > metadata.m_getPutInfo = GetPutInfo(metadata.m_getPutInfo.resolveMode(), newResolveType, metadata.m_getPutInfo.initializationMode()); >- } else if (scope->isGlobalLexicalEnvironment()) { >+ break; >+ } >+ FALLTHROUGH; >+ } >+ case GlobalProperty: >+ case GlobalPropertyWithVarInjectionChecks: { >+ // Global Lexical Binding Epoch is changed. Update op_get_from_scope from GlobalProperty to GlobalLexicalVar. >+ if (scope->isGlobalLexicalEnvironment()) { > JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(scope); >- ResolveType newResolveType = resolveType == UnresolvedProperty ? GlobalLexicalVar : GlobalLexicalVarWithVarInjectionChecks; >+ ResolveType newResolveType = needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar; > SymbolTableEntry entry = globalLexicalEnvironment->symbolTable()->get(ident.impl()); > ASSERT(!entry.isNull()); > ConcurrentJSLocker locker(exec->codeBlock()->m_lock); > metadata.m_getPutInfo = GetPutInfo(metadata.m_getPutInfo.resolveMode(), newResolveType, metadata.m_getPutInfo.initializationMode()); > metadata.m_watchpointSet = entry.watchpointSet(); > metadata.m_operand = reinterpret_cast<uintptr_t>(globalLexicalEnvironment->variableAt(entry.scopeOffset()).slot()); >+ return; > } >+ break; >+ } >+ default: >+ return; > } > > // Covers implicit globals. Since they don't exist until they first execute, we didn't know how to cache them at compile time. >diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >index bb70cf56b49347abeaf23c5a2639255a9772a0fd..07f4c7d877343f2722a72e09c9914ff793f7ba01 100644 >--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >@@ -1852,19 +1852,19 @@ const HashSet<String>& JSGlobalObject::intlPluralRulesAvailableLocales() > } > #endif // ENABLE(INTL) > >-void JSGlobalObject::notifyLexicalBindingShadowing(VM& vm, const IdentifierSet& set) >+void JSGlobalObject::bumpGlobalLexicalBindingEpoch(VM& vm) > { > auto scope = DECLARE_THROW_SCOPE(vm); >-#if ENABLE(DFG_JIT) >- for (const auto& key : set) >- ensureReferencedPropertyWatchpointSet(key.get()).fireAll(vm, "Lexical binding shadows the existing global properties"); >-#endif >- vm.heap.codeBlockSet().iterate([&] (CodeBlock* codeBlock) { >- if (codeBlock->globalObject() != this) >- return; >- codeBlock->notifyLexicalBindingShadowing(vm, set); >- scope.assertNoException(); >- }); >+ if (++m_globalLexicalBindingEpoch == Options::thresholdForGlobalLexicalBindingEpoch()) { >+ // Since the epoch overflows or actual shadowing happens, we should rewrite all the CodeBlock to adjust to the newly started generation. >+ m_globalLexicalBindingEpoch = 1; >+ vm.heap.codeBlockSet().iterate([&] (CodeBlock* codeBlock) { >+ if (codeBlock->globalObject() != this) >+ return; >+ codeBlock->notifyLexicalBindingUpdate(vm); >+ scope.assertNoException(); >+ }); >+ } > scope.release(); > } > >@@ -1889,13 +1889,15 @@ bool JSGlobalObject::hasInteractiveDebugger() const > } > > #if ENABLE(DFG_JIT) >-WatchpointSet* JSGlobalObject::getReferencedPropertyWatchpointSet(UniquedStringImpl* uid) >+WatchpointSet* JSGlobalObject::getReferencedPropertyWatchpointSetConcurrently(UniquedStringImpl* uid) > { >+ ConcurrentJSLocker locker(m_referencedGlobalPropertyWatchpointSetsLock); > return m_referencedGlobalPropertyWatchpointSets.get(uid); > } > > WatchpointSet& JSGlobalObject::ensureReferencedPropertyWatchpointSet(UniquedStringImpl* uid) > { >+ ConcurrentJSLocker locker(m_referencedGlobalPropertyWatchpointSetsLock); > return m_referencedGlobalPropertyWatchpointSets.ensure(uid, [] { > return WatchpointSet::create(IsWatched); > }).iterator->value.get(); >diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h >index 3a1382d57979cc29b16fb68e93f4fb7d97e6e39d..713d2dd3e94e9657ef0ac713b666d831efbc0f70 100644 >--- a/Source/JavaScriptCore/runtime/JSGlobalObject.h >+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h >@@ -486,10 +486,12 @@ class JSGlobalObject : public JSSegmentedVariableObject { > #if ENABLE(DFG_JIT) > using ReferencedGlobalPropertyWatchpointSets = HashMap<RefPtr<UniquedStringImpl>, Ref<WatchpointSet>, IdentifierRepHash>; > ReferencedGlobalPropertyWatchpointSets m_referencedGlobalPropertyWatchpointSets; >+ ConcurrentJSLock m_referencedGlobalPropertyWatchpointSetsLock; > #endif > > bool m_evalEnabled { true }; > bool m_webAssemblyEnabled { true }; >+ unsigned m_globalLexicalBindingEpoch { 1 }; > String m_evalDisabledErrorMessage; > String m_webAssemblyDisabledErrorMessage; > RuntimeFlags m_runtimeFlags; >@@ -522,7 +524,7 @@ class JSGlobalObject : public JSSegmentedVariableObject { > const RuntimeFlags& runtimeFlags() const { return m_runtimeFlags; } > > #if ENABLE(DFG_JIT) >- WatchpointSet* getReferencedPropertyWatchpointSet(UniquedStringImpl*); >+ WatchpointSet* getReferencedPropertyWatchpointSetConcurrently(UniquedStringImpl*); > WatchpointSet& ensureReferencedPropertyWatchpointSet(UniquedStringImpl*); > #endif > >@@ -751,7 +753,10 @@ class JSGlobalObject : public JSSegmentedVariableObject { > const HashSet<String>& intlPluralRulesAvailableLocales(); > #endif // ENABLE(INTL) > >- void notifyLexicalBindingShadowing(VM&, const IdentifierSet&); >+ void bumpGlobalLexicalBindingEpoch(VM&); >+ unsigned globalLexicalBindingEpoch() const { return m_globalLexicalBindingEpoch; } >+ static ptrdiff_t globalLexicalBindingEpochOffset() { return OBJECT_OFFSETOF(JSGlobalObject, m_globalLexicalBindingEpoch); } >+ unsigned* addressOfGlobalLexicalBindingEpoch() { return &m_globalLexicalBindingEpoch; } > > void setConsoleClient(ConsoleClient* consoleClient) { m_consoleClient = consoleClient; } > ConsoleClient* consoleClient() const { return m_consoleClient; } >diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h >index e67d9393e0c268f8917c98229c1af348ae21f361..4c9f6b6b4e169adcca81249349730212f1382137 100644 >--- a/Source/JavaScriptCore/runtime/Options.h >+++ b/Source/JavaScriptCore/runtime/Options.h >@@ -508,6 +508,7 @@ constexpr bool enableWebAssemblyStreamingApi = false; > v(bool, traceLLIntExecution, false, Configurable, nullptr) \ > v(bool, traceLLIntSlowPath, false, Configurable, nullptr) \ > v(bool, traceBaselineJITExecution, false, Normal, nullptr) \ >+ v(unsigned, thresholdForGlobalLexicalBindingEpoch, UINT_MAX, Normal, "Threshold for global lexical binding epoch. If the epoch reaches to this value, CodeBlock metadata for scope operations will be revised globally. It needs to be greater than 1.") \ > > > enum OptionEquivalence { >diff --git a/Source/JavaScriptCore/runtime/ProgramExecutable.cpp b/Source/JavaScriptCore/runtime/ProgramExecutable.cpp >index 1c21967fc1d82182f7335a0d7f7aba99ecdc5b2e..d4b588c5df348dfd72be586fcd60830aa014fa82 100644 >--- a/Source/JavaScriptCore/runtime/ProgramExecutable.cpp >+++ b/Source/JavaScriptCore/runtime/ProgramExecutable.cpp >@@ -107,7 +107,6 @@ JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callF > JSGlobalLexicalEnvironment* globalLexicalEnvironment = globalObject->globalLexicalEnvironment(); > const VariableEnvironment& variableDeclarations = unlinkedCodeBlock->variableDeclarations(); > const VariableEnvironment& lexicalDeclarations = unlinkedCodeBlock->lexicalDeclarations(); >- IdentifierSet shadowedProperties; > // The ES6 spec says that no vars/global properties/let/const can be duplicated in the global scope. > // This carried out section 15.1.8 of the ES6 spec: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-globaldeclarationinstantiation > { >@@ -131,11 +130,9 @@ JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callF > // Lexical bindings can shadow global properties if the given property's attribute is configurable. > // https://tc39.github.io/ecma262/#sec-globaldeclarationinstantiation step 5-c, `hasRestrictedGlobal` becomes false > // However we may emit GlobalProperty look up in bytecodes already and it may cache the value for the global scope. >- // To make it invalid, we iterate all the CodeBlocks and rewrite the instruction to convert GlobalProperty to GlobalLexicalVar. >- // 1. In LLInt, we always check metadata's resolveType. So rewritten instruction just works. >- // 2. In Baseline JIT, we check metadata's resolveType in GlobalProperty case so that we can notice once it is changed. >+ // To make it invalid, >+ // 1. In LLInt and Baseline, we bump the global lexical binding epoch and it works. > // 3. In DFG and FTL, we watch the watchpoint and jettison once it is fired. >- shadowedProperties.add(entry.key.get()); > break; > case GlobalPropertyLookUpStatus::NotFound: > break; >@@ -206,12 +203,15 @@ JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callF > RELEASE_ASSERT(offsetForAssert == offset); > } > } >- >- if (!shadowedProperties.isEmpty()) { >- globalObject->notifyLexicalBindingShadowing(vm, WTFMove(shadowedProperties)); >- throwScope.assertNoException(); >+ if (lexicalDeclarations.size()) { >+ for (auto& entry : lexicalDeclarations) { >+ // If WatchpointSet exists, just fire it. Since DFG WatchpointSet addition is also done on the main thread, we can sync them. >+ // So that we do not create WatchpointSet here. DFG will create if necessary on the main thread. >+ if (auto* watchpointSet = globalObject->getReferencedPropertyWatchpointSetConcurrently(entry.key.get())) >+ watchpointSet->fireAll(vm, "Lexical binding shadows the existing global properties"); >+ } >+ globalObject->bumpGlobalLexicalBindingEpoch(vm); > } >- > return nullptr; > } > >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index 777a853bfe709f942ac64f8b41717e0a94ab67b0..7f5617065c0c50a6b6240ec5680126364cd39b53 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,23 @@ >+2019-01-18 Yusuke Suzuki <yusukesuzuki@slowstart.org> >+ >+ [JSC] Invalidate old scope operations using global lexical binding epoch >+ https://bugs.webkit.org/show_bug.cgi?id=193603 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * stress/scope-operation-cache-global-property-before-deleting.js: Added. >+ (shouldThrow): >+ (bar): >+ * stress/scope-operation-cache-global-property-bump-counter.js: Added. >+ (shouldBe): >+ (get1): >+ (get2): >+ (get1If): >+ (get2If): >+ * stress/scope-operation-cache-global-property-even-if-it-fails.js: Added. >+ (shouldThrow): >+ (foo): >+ > 2019-01-17 Saam barati <sbarati@apple.com> > > StringObjectUse should not be a structure check for the original string object structure >diff --git a/JSTests/stress/scope-operation-cache-global-property-before-deleting.js b/JSTests/stress/scope-operation-cache-global-property-before-deleting.js >new file mode 100644 >index 0000000000000000000000000000000000000000..ca9fe0d51c584e85c0f266aea21487e0d93d4122 >--- /dev/null >+++ b/JSTests/stress/scope-operation-cache-global-property-before-deleting.js >@@ -0,0 +1,27 @@ >+function shouldThrow(func, errorMessage) { >+ var errorThrown = false; >+ var error = null; >+ try { >+ func(); >+ } catch (e) { >+ errorThrown = true; >+ error = e; >+ } >+ if (!errorThrown) >+ throw new Error('not thrown'); >+ if (String(error) !== errorMessage) >+ throw new Error(`bad error: ${String(error)}`); >+} >+noInline(shouldThrow); >+ >+function bar() >+{ >+ foo = 42; >+} >+ >+bar(); >+bar(); >+delete globalThis.foo; >+$.evalScript(`const foo = 50`); >+ >+shouldThrow(() => bar(), `TypeError: Attempted to assign to readonly property.`); >diff --git a/JSTests/stress/scope-operation-cache-global-property-bump-counter.js b/JSTests/stress/scope-operation-cache-global-property-bump-counter.js >new file mode 100644 >index 0000000000000000000000000000000000000000..d1dcdd06d2089bd9f095a1a18219f6be7f9bc6f8 >--- /dev/null >+++ b/JSTests/stress/scope-operation-cache-global-property-bump-counter.js >@@ -0,0 +1,58 @@ >+//@ runDefault("--thresholdForGlobalLexicalBindingEpoch=2") >+ >+function shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error('bad value: ' + actual); >+} >+noInline(shouldBe); >+ >+foo1 = 1; >+foo2 = 2; >+function get1() { >+ return foo1; >+} >+noInline(get1); >+ >+function get2() { >+ return foo2; >+} >+noInline(get2); >+ >+function get1If(condition) { >+ if (condition) >+ return foo1; >+ return -1; >+} >+noInline(get1If); >+ >+function get2If(condition) { >+ if (condition) >+ return foo2; >+ return -1; >+} >+noInline(get2If); >+ >+for (var i = 0; i < 1e5; ++i) { >+ shouldBe(get1(), 1); >+ shouldBe(get2(), 2); >+ shouldBe(get1(), 1); >+ shouldBe(get2(), 2); >+ shouldBe(get1If(true), 1); >+ shouldBe(get2If(true), 2); >+ shouldBe(get1If(false), -1); >+ shouldBe(get2If(false), -1); >+} >+ >+$.evalScript(`const foo1 = 41;`); >+$.evalScript(`const foo2 = 42;`); >+ >+for (var i = 0; i < 1e3; ++i) { >+ shouldBe(get1(), 41); >+ shouldBe(get2(), 42); >+ shouldBe(get1(), 41); >+ shouldBe(get2(), 42); >+ shouldBe(get1If(false), -1); >+ shouldBe(get2If(false), -1); >+} >+shouldBe(get1If(true), 41); >+shouldBe(get2If(true), 42); >diff --git a/JSTests/stress/scope-operation-cache-global-property-even-if-it-fails.js b/JSTests/stress/scope-operation-cache-global-property-even-if-it-fails.js >new file mode 100644 >index 0000000000000000000000000000000000000000..51dac811a38bfb252eb7f6ccb3aa40976146c655 >--- /dev/null >+++ b/JSTests/stress/scope-operation-cache-global-property-even-if-it-fails.js >@@ -0,0 +1,23 @@ >+function shouldThrow(func, errorMessage) { >+ var errorThrown = false; >+ var error = null; >+ try { >+ func(); >+ } catch (e) { >+ errorThrown = true; >+ error = e; >+ } >+ if (!errorThrown) >+ throw new Error('not thrown'); >+ if (String(error) !== errorMessage) >+ throw new Error(`bad error: ${String(error)}`); >+} >+noInline(shouldThrow); >+ >+function foo() { >+ bar = 4; >+} >+Object.preventExtensions(this); >+foo(); >+$.evalScript('const bar = 3;'); >+shouldThrow(() => foo(), `TypeError: Attempted to assign to readonly property.`);
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 193603
:
359573
|
359575
|
359576
|
359578
|
359579
|
359580
|
359625
|
359657
|
359661
|
359662
|
359720
|
359846