WebKit Bugzilla
Attachment 359662 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 for landing
bug-193603-20190120170108.patch (text/plain), 56.46 KB, created by
Yusuke Suzuki
on 2019-01-20 17:01:08 PST
(
hide
)
Description:
Patch for landing
Filename:
MIME Type:
Creator:
Yusuke Suzuki
Created:
2019-01-20 17:01:08 PST
Size:
56.46 KB
patch
obsolete
>Subversion Revision: 240217 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 8e9e5844a8e04cf906fddea1329e58b3d3c7ccd2..40275a8b234f5cd3b2a816090a149b2f4b6683f3 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,85 @@ >+2019-01-20 Yusuke Suzuki <ysuzuki@apple.com> >+ >+ [JSC] Invalidate old scope operations using global lexical binding epoch >+ https://bugs.webkit.org/show_bug.cgi?id=193603 >+ <rdar://problem/47380869> >+ >+ Reviewed by Saam Barati. >+ >+ 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::getReferencedPropertyWatchpointSet): >+ (JSC::JSGlobalObject::ensureReferencedPropertyWatchpointSet): >+ (JSC::JSGlobalObject::notifyLexicalBindingShadowing): Deleted. >+ * runtime/JSGlobalObject.h: >+ (JSC::JSGlobalObject::globalLexicalBindingEpoch const): >+ (JSC::JSGlobalObject::globalLexicalBindingEpochOffset): >+ (JSC::JSGlobalObject::addressOfGlobalLexicalBindingEpoch): >+ * runtime/Options.cpp: >+ (JSC::correctOptions): >+ (JSC::Options::initialize): >+ (JSC::Options::setOptions): >+ (JSC::Options::setOptionWithoutAlias): >+ * runtime/Options.h: >+ * runtime/ProgramExecutable.cpp: >+ (JSC::ProgramExecutable::initializeGlobalProperties): >+ > 2019-01-20 Yusuke Suzuki <yusukesuzuki@slowstart.org> > > [JSC] Shrink data structure size in JSC/heap >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 1b5d222efd5052879b316c1d0ead53907221443d..dccc4111487375dc18b6addb539a1086772eaa51 100644 >--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp >+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >@@ -2668,18 +2668,23 @@ void CodeBlock::tallyFrequentExitSites() > } > #endif // ENABLE(DFG_JIT) > >-void CodeBlock::notifyLexicalBindingShadowing(VM& vm, const IdentifierSet& set) >+void CodeBlock::notifyLexicalBindingUpdate() > { > // 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(); >- >- auto scope = DECLARE_THROW_SCOPE(vm); >+ JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(globalObject->globalScope()); >+ SymbolTable* symbolTable = globalLexicalEnvironment->symbolTable(); > > 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 +2694,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); >- } >+ if (isShadowed(ident.impl())) >+ metadata.m_globalLexicalBindingEpoch = 0; >+ else >+ metadata.m_globalLexicalBindingEpoch = globalObject->globalLexicalBindingEpoch(); > } > 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()); >- } >- } >- 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 c748fc07c2711389f2703454c226e5c7cb81c06a..c747426b38e566ebb9e842ce23ed59384eb0ae20 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(); > > 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..ba6841ddc42f93442302e15e444011a2031e5e6d 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 an existing global property"); >+ 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..5a60afb9e4e3b0ca6bfa0aeb601dd31a858863a5 100644 >--- a/Source/JavaScriptCore/dfg/DFGGraph.cpp >+++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp >@@ -1058,6 +1058,20 @@ 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 we still have stale metadata here since we have not yet executed this bytecode operation since the invalidation. Just emitting ForceOSRExit to update the >+ // metadata when it reaches to this code. >+ if (auto* watchpoint = globalObject->getReferencedPropertyWatchpointSet(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..2f4f32e106b9db546b84b24f7b98996c9d3db481 100644 >--- a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp >+++ b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp >@@ -766,7 +766,17 @@ void JIT::emit_op_resolve_scope(const Instruction* currentInstruction) > auto emitCode = [&] (ResolveType resolveType) { > switch (resolveType) { > case GlobalProperty: >- case GlobalPropertyWithVarInjectionChecks: >+ case GlobalPropertyWithVarInjectionChecks: { >+ JSScope* constantScope = JSScope::constantScopeForCodeBlock(resolveType, m_codeBlock); >+ RELEASE_ASSERT(constantScope); >+ emitVarInjectionCheck(needsVarInjectionChecks(resolveType)); >+ load32(&metadata.m_globalLexicalBindingEpoch, regT1); >+ addSlowCase(branch32(NotEqual, AbsoluteAddress(m_codeBlock->globalObject()->addressOfGlobalLexicalBindingEpoch()), regT1)); >+ move(TrustedImmPtr(constantScope), regT0); >+ emitPutVirtualRegister(dst); >+ break; >+ } >+ > case GlobalVar: > case GlobalVarWithVarInjectionChecks: > case GlobalLexicalVar: >@@ -799,11 +809,17 @@ void JIT::emit_op_resolve_scope(const Instruction* currentInstruction) > switch (resolveType) { > case GlobalProperty: > case GlobalPropertyWithVarInjectionChecks: { >- // Since these GlobalProperty can be changed to GlobalLexicalVar, we should load the value from metadata. >- JSScope** constantScopeSlot = metadata.m_constantScope.slot(); >- emitVarInjectionCheck(needsVarInjectionChecks(resolveType)); >- loadPtr(constantScopeSlot, regT0); >- emitPutVirtualRegister(dst); >+ JumpList skipToEnd; >+ load32(&metadata.m_resolveType, regT0); >+ >+ Jump notGlobalProperty = branch32(NotEqual, regT0, TrustedImm32(resolveType)); >+ emitCode(resolveType); >+ skipToEnd.append(jump()); >+ >+ notGlobalProperty.link(this); >+ emitCode(needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar); >+ >+ skipToEnd.link(this); > break; > } > case UnresolvedProperty: >diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp >index 33d6ae460864376a0540b69d1564be5efac1369f..e2da115ecd3cdf13e9db1d0745de37bef8dc4287 100644 >--- a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp >+++ b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp >@@ -769,7 +769,18 @@ void JIT::emit_op_resolve_scope(const Instruction* currentInstruction) > auto emitCode = [&] (ResolveType resolveType) { > switch (resolveType) { > case GlobalProperty: >- case GlobalPropertyWithVarInjectionChecks: >+ case GlobalPropertyWithVarInjectionChecks: { >+ JSScope* constantScope = JSScope::constantScopeForCodeBlock(resolveType, m_codeBlock); >+ RELEASE_ASSERT(constantScope); >+ emitVarInjectionCheck(needsVarInjectionChecks(resolveType)); >+ load32(&metadata.m_globalLexicalBindingEpoch, regT1); >+ addSlowCase(branch32(NotEqual, AbsoluteAddress(m_codeBlock->globalObject()->addressOfGlobalLexicalBindingEpoch()), regT1)); >+ move(TrustedImm32(JSValue::CellTag), regT1); >+ move(TrustedImmPtr(constantScope), regT0); >+ emitStore(dst, regT1, regT0); >+ break; >+ } >+ > case GlobalVar: > case GlobalVarWithVarInjectionChecks: > case GlobalLexicalVar: >@@ -803,12 +814,17 @@ void JIT::emit_op_resolve_scope(const Instruction* currentInstruction) > switch (resolveType) { > case GlobalProperty: > case GlobalPropertyWithVarInjectionChecks: { >- // Since these GlobalProperty can be changed to GlobalLexicalVar, we should load the value from metadata. >- JSScope** constantScopeSlot = metadata.m_constantScope.slot(); >- emitVarInjectionCheck(needsVarInjectionChecks(resolveType)); >- move(TrustedImm32(JSValue::CellTag), regT1); >- loadPtr(constantScopeSlot, regT0); >- emitStore(dst, regT1, regT0); >+ JumpList skipToEnd; >+ load32(&metadata.m_resolveType, regT0); >+ >+ Jump notGlobalProperty = branch32(NotEqual, regT0, TrustedImm32(resolveType)); >+ emitCode(resolveType); >+ skipToEnd.append(jump()); >+ >+ notGlobalProperty.link(this); >+ emitCode(needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar); >+ >+ skipToEnd.link(this); > break; > } > case UnresolvedProperty: >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 4e37a09603f73d979673498df99706ccc1a1fd93..bd7d02c9c9d45490ff7b28569d1aaf76636c64fe 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..bbbadab67fdf6fcad4cac0664cdc8e60bf5e4b2e 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: >+ 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..e540048ed8a8b790d9d86dd91901fef85f4c9a7f 100644 >--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >@@ -1852,20 +1852,17 @@ 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(); >- }); >- scope.release(); >+ if (++m_globalLexicalBindingEpoch == Options::thresholdForGlobalLexicalBindingEpoch()) { >+ // Since the epoch overflows, 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(); >+ }); >+ } > } > > void JSGlobalObject::queueMicrotask(Ref<Microtask>&& task) >@@ -1891,11 +1888,13 @@ bool JSGlobalObject::hasInteractiveDebugger() const > #if ENABLE(DFG_JIT) > WatchpointSet* JSGlobalObject::getReferencedPropertyWatchpointSet(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..d52d0fbfc9828e69666af5b52014b8f39c6e56ef 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; >@@ -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.cpp b/Source/JavaScriptCore/runtime/Options.cpp >index 962d87e9926dace3766f5973d096d88227e1de71..8b05ca82300bc79a229ac60f86bb45667eefda5e 100644 >--- a/Source/JavaScriptCore/runtime/Options.cpp >+++ b/Source/JavaScriptCore/runtime/Options.cpp >@@ -376,6 +376,13 @@ static void overrideDefaults() > #endif > } > >+static void correctOptions() >+{ >+ unsigned thresholdForGlobalLexicalBindingEpoch = Options::thresholdForGlobalLexicalBindingEpoch(); >+ if (thresholdForGlobalLexicalBindingEpoch == 0 || thresholdForGlobalLexicalBindingEpoch == 1) >+ Options::thresholdForGlobalLexicalBindingEpoch() = UINT_MAX; >+} >+ > static void recomputeDependentOptions() > { > #if !defined(NDEBUG) >@@ -572,6 +579,8 @@ void Options::initialize() > #if 0 > ; // Deconfuse editors that do auto indentation > #endif >+ >+ correctOptions(); > > recomputeDependentOptions(); > >@@ -705,6 +714,8 @@ bool Options::setOptions(const char* optionsStr) > } > } > >+ correctOptions(); >+ > recomputeDependentOptions(); > > dumpOptionsIfNeeded(); >@@ -741,6 +752,7 @@ bool Options::setOptionWithoutAlias(const char* arg) > bool success = parse(valueStr, value); \ > if (success) { \ > name_() = value; \ >+ correctOptions(); \ > recomputeDependentOptions(); \ > return true; \ > } \ >diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h >index 48f6e62a7a02b941a9c31e881ca6cb1f29d224b6..5421a839dfde22245f75a2a0e900de8da5dbdf40 100644 >--- a/Source/JavaScriptCore/runtime/Options.h >+++ b/Source/JavaScriptCore/runtime/Options.h >@@ -510,6 +510,7 @@ constexpr bool enableWebAssemblyStreamingApi = false; > v(bool, traceBaselineJITExecution, false, Normal, nullptr) \ > v(optionString, diskCachePath, nullptr, Restricted, "") \ > v(bool, forceDiskCache, false, Restricted, "") \ >+ 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..1a3f09242ed8d2f03dd8232343b91392e88cb52e 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,18 @@ JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callF > RELEASE_ASSERT(offsetForAssert == offset); > } > } >- >- if (!shadowedProperties.isEmpty()) { >- globalObject->notifyLexicalBindingShadowing(vm, WTFMove(shadowedProperties)); >- throwScope.assertNoException(); >+ if (lexicalDeclarations.size()) { >+#if ENABLE(DFG_JIT) >+ 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. >+ // And it will only create not-invalidated watchpoint set if the global lexical environment binding doesn't exist, which is why this code works. >+ if (auto* watchpointSet = globalObject->getReferencedPropertyWatchpointSet(entry.key.get())) >+ watchpointSet->fireAll(vm, "Lexical binding shadows an existing global property"); >+ } >+#endif >+ globalObject->bumpGlobalLexicalBindingEpoch(vm); > } >- > return nullptr; > } > >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index 777a853bfe709f942ac64f8b41717e0a94ab67b0..d07e6741e5232d62d6bfb4b852f07855a8652c70 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,25 @@ >+2019-01-20 Yusuke Suzuki <ysuzuki@apple.com> >+ >+ [JSC] Invalidate old scope operations using global lexical binding epoch >+ https://bugs.webkit.org/show_bug.cgi?id=193603 >+ <rdar://problem/47380869> >+ >+ Reviewed by Saam Barati. >+ >+ * stress/let-lexical-binding-shadow-existing-global-property-ftl.js: >+ * 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/let-lexical-binding-shadow-existing-global-property-ftl.js b/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js >index 20b4322e07d016e12fd6c03584b6dc32c240da76..b47b72cb60de219139c29ae7b8d29eb44d1b5b33 100644 >--- a/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js >+++ b/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js >@@ -40,6 +40,7 @@ function get() { > shouldBe(get(), 3); > > foo(); >+shouldBe(globalThis.bar, 4); > shouldBe(bar, 4); > shouldBe(get(), 4); > >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