Diff
Modified: trunk/JSTests/ChangeLog (240219 => 240220)
--- trunk/JSTests/ChangeLog 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/JSTests/ChangeLog 2019-01-21 01:39:06 UTC (rev 240220)
@@ -1,3 +1,25 @@
+2019-01-20 Yusuke Suzuki <ysuz...@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 <sbar...@apple.com>
StringObjectUse should not be a structure check for the original string object structure
Modified: trunk/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js (240219 => 240220)
--- trunk/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js 2019-01-21 01:39:06 UTC (rev 240220)
@@ -40,6 +40,7 @@
shouldBe(get(), 3);
foo();
+shouldBe(globalThis.bar, 4);
shouldBe(bar, 4);
shouldBe(get(), 4);
Added: trunk/JSTests/stress/scope-operation-cache-global-property-before-deleting.js (0 => 240220)
--- trunk/JSTests/stress/scope-operation-cache-global-property-before-deleting.js (rev 0)
+++ trunk/JSTests/stress/scope-operation-cache-global-property-before-deleting.js 2019-01-21 01:39:06 UTC (rev 240220)
@@ -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.`);
Added: trunk/JSTests/stress/scope-operation-cache-global-property-bump-counter.js (0 => 240220)
--- trunk/JSTests/stress/scope-operation-cache-global-property-bump-counter.js (rev 0)
+++ trunk/JSTests/stress/scope-operation-cache-global-property-bump-counter.js 2019-01-21 01:39:06 UTC (rev 240220)
@@ -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);
Added: trunk/JSTests/stress/scope-operation-cache-global-property-even-if-it-fails.js (0 => 240220)
--- trunk/JSTests/stress/scope-operation-cache-global-property-even-if-it-fails.js (rev 0)
+++ trunk/JSTests/stress/scope-operation-cache-global-property-even-if-it-fails.js 2019-01-21 01:39:06 UTC (rev 240220)
@@ -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.`);
Modified: trunk/Source/_javascript_Core/ChangeLog (240219 => 240220)
--- trunk/Source/_javascript_Core/ChangeLog 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/ChangeLog 2019-01-21 01:39:06 UTC (rev 240220)
@@ -1,3 +1,85 @@
+2019-01-20 Yusuke Suzuki <ysuz...@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 <yusukesuz...@slowstart.org>
[JSC] Shrink data structure size in JSC/heap
Modified: trunk/Source/_javascript_Core/bytecode/BytecodeList.rb (240219 => 240220)
--- trunk/Source/_javascript_Core/bytecode/BytecodeList.rb 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeList.rb 2019-01-21 01:39:06 UTC (rev 240220)
@@ -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
Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp (240219 => 240220)
--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2019-01-21 01:39:06 UTC (rev 240220)
@@ -2668,7 +2668,7 @@
}
#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
@@ -2675,11 +2675,16 @@
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 +2694,13 @@
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;
}
Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.h (240219 => 240220)
--- trunk/Source/_javascript_Core/bytecode/CodeBlock.h 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.h 2019-01-21 01:39:06 UTC (rev 240220)
@@ -195,7 +195,7 @@
void visitChildren(SlotVisitor&);
void finalizeUnconditionally(VM&);
- void notifyLexicalBindingShadowing(VM&, const IdentifierSet&);
+ void notifyLexicalBindingUpdate();
void dumpSource();
void dumpSource(PrintStream&);
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (240219 => 240220)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2019-01-21 01:39:06 UTC (rev 240220)
@@ -6167,9 +6167,10 @@
// https://bugs.webkit.org/show_bug.cgi?id=193347
if (m_inlineStackTop->m_codeBlock->scriptMode() != JSParserScriptMode::Module) {
if (resolveType == GlobalProperty || resolveType == GlobalPropertyWithVarInjectionChecks) {
+ JSGlobalObject* globalObject = m_inlineStackTop->m_codeBlock->globalObject();
unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[bytecode.m_var];
- JSGlobalObject* globalObject = m_inlineStackTop->m_codeBlock->globalObject();
- m_graph.globalProperties().addLazily(DesiredGlobalProperty(globalObject, identifierNumber));
+ if (!m_graph.watchGlobalProperty(globalObject, identifierNumber))
+ addToGraph(ForceOSRExit);
}
}
@@ -6281,8 +6282,10 @@
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 @@
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)
Modified: trunk/Source/_javascript_Core/dfg/DFGDesiredGlobalProperties.cpp (240219 => 240220)
--- trunk/Source/_javascript_Core/dfg/DFGDesiredGlobalProperties.cpp 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/dfg/DFGDesiredGlobalProperties.cpp 2019-01-21 01:39:06 UTC (rev 240220)
@@ -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)
Modified: trunk/Source/_javascript_Core/dfg/DFGDesiredGlobalProperties.h (240219 => 240220)
--- trunk/Source/_javascript_Core/dfg/DFGDesiredGlobalProperties.h 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/dfg/DFGDesiredGlobalProperties.h 2019-01-21 01:39:06 UTC (rev 240220)
@@ -47,7 +47,7 @@
m_set.add(WTFMove(property));
}
- bool isStillValidOnMainThread(DesiredIdentifiers&);
+ bool isStillValidOnMainThread(VM&, DesiredIdentifiers&);
void reallyAdd(CodeBlock*, DesiredIdentifiers&, CommonData&);
Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.cpp (240219 => 240220)
--- trunk/Source/_javascript_Core/dfg/DFGGraph.cpp 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.cpp 2019-01-21 01:39:06 UTC (rev 240220)
@@ -1058,6 +1058,20 @@
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);
Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.h (240219 => 240220)
--- trunk/Source/_javascript_Core/dfg/DFGGraph.h 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h 2019-01-21 01:39:06 UTC (rev 240220)
@@ -793,6 +793,8 @@
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);
Modified: trunk/Source/_javascript_Core/dfg/DFGPlan.cpp (240219 => 240220)
--- trunk/Source/_javascript_Core/dfg/DFGPlan.cpp 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/dfg/DFGPlan.cpp 2019-01-21 01:39:06 UTC (rev 240220)
@@ -573,7 +573,7 @@
bool Plan::isStillValidOnMainThread()
{
- return m_globalProperties.isStillValidOnMainThread(m_identifiers);
+ return m_globalProperties.isStillValidOnMainThread(*m_vm, m_identifiers);
}
CompilationResult Plan::finalizeWithoutNotifyingCallback()
Modified: trunk/Source/_javascript_Core/jit/JITPropertyAccess.cpp (240219 => 240220)
--- trunk/Source/_javascript_Core/jit/JITPropertyAccess.cpp 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/jit/JITPropertyAccess.cpp 2019-01-21 01:39:06 UTC (rev 240220)
@@ -766,7 +766,17 @@
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 @@
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:
Modified: trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp (240219 => 240220)
--- trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp 2019-01-21 01:39:06 UTC (rev 240220)
@@ -769,7 +769,18 @@
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 @@
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:
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm (240219 => 240220)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm 2019-01-21 01:39:06 UTC (rev 240220)
@@ -2092,11 +2092,20 @@
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 @@
#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 @@
.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
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm (240219 => 240220)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm 2019-01-21 01:39:06 UTC (rev 240220)
@@ -2150,11 +2150,20 @@
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 @@
#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 @@
.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 @@
#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 @@
.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 @@
.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 @@
.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()
Modified: trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp (240219 => 240220)
--- trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp 2019-01-21 01:39:06 UTC (rev 240220)
@@ -1067,7 +1067,11 @@
// 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);
@@ -1074,23 +1078,21 @@
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);
}
Modified: trunk/Source/_javascript_Core/runtime/CommonSlowPaths.h (240219 => 240220)
--- trunk/Source/_javascript_Core/runtime/CommonSlowPaths.h 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/runtime/CommonSlowPaths.h 2019-01-21 01:39:06 UTC (rev 240220)
@@ -124,19 +124,25 @@
// 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());
@@ -143,9 +149,14 @@
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,15 +187,24 @@
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);
@@ -191,8 +211,13 @@
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.
if (resolveType == GlobalProperty || resolveType == GlobalPropertyWithVarInjectionChecks) {
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (240219 => 240220)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2019-01-21 01:39:06 UTC (rev 240220)
@@ -1852,20 +1852,17 @@
}
#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 @@
#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();
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (240219 => 240220)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h 2019-01-21 01:39:06 UTC (rev 240220)
@@ -486,10 +486,12 @@
#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 @@
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; }
Modified: trunk/Source/_javascript_Core/runtime/Options.cpp (240219 => 240220)
--- trunk/Source/_javascript_Core/runtime/Options.cpp 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/runtime/Options.cpp 2019-01-21 01:39:06 UTC (rev 240220)
@@ -376,6 +376,13 @@
#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 @@
#if 0
; // Deconfuse editors that do auto indentation
#endif
+
+ correctOptions();
recomputeDependentOptions();
@@ -705,6 +714,8 @@
}
}
+ correctOptions();
+
recomputeDependentOptions();
dumpOptionsIfNeeded();
@@ -741,6 +752,7 @@
bool success = parse(valueStr, value); \
if (success) { \
name_() = value; \
+ correctOptions(); \
recomputeDependentOptions(); \
return true; \
} \
Modified: trunk/Source/_javascript_Core/runtime/Options.h (240219 => 240220)
--- trunk/Source/_javascript_Core/runtime/Options.h 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/runtime/Options.h 2019-01-21 01:39:06 UTC (rev 240220)
@@ -510,6 +510,7 @@
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 {
Modified: trunk/Source/_javascript_Core/runtime/ProgramExecutable.cpp (240219 => 240220)
--- trunk/Source/_javascript_Core/runtime/ProgramExecutable.cpp 2019-01-21 01:26:37 UTC (rev 240219)
+++ trunk/Source/_javascript_Core/runtime/ProgramExecutable.cpp 2019-01-21 01:39:06 UTC (rev 240220)
@@ -107,7 +107,6 @@
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 @@
// 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 @@
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;
}