Title: [240254] trunk
Revision
240254
Author
commit-qu...@webkit.org
Date
2019-01-22 09:48:08 -0800 (Tue, 22 Jan 2019)

Log Message

[JSC] Invalidate old scope operations using global lexical binding epoch
https://bugs.webkit.org/show_bug.cgi?id=193603
<rdar://problem/47380869>

Patch by Yusuke Suzuki <ysuz...@apple.com> on 2019-01-22
Reviewed by Saam Barati.

JSTests:

* 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):

Source/_javascript_Core:

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::finishCreation):
(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):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (240253 => 240254)


--- trunk/JSTests/ChangeLog	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/JSTests/ChangeLog	2019-01-22 17:48:08 UTC (rev 240254)
@@ -1,3 +1,25 @@
+2019-01-22  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-21  Yusuke Suzuki  <ysuz...@apple.com>
 
         Unreviewed, roll out r240220 due to date-format-xparb regression

Modified: trunk/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js (240253 => 240254)


--- trunk/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 => 240254)


--- 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-22 17:48:08 UTC (rev 240254)
@@ -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 => 240254)


--- 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-22 17:48:08 UTC (rev 240254)
@@ -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 => 240254)


--- 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-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/ChangeLog	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/ChangeLog	2019-01-22 17:48:08 UTC (rev 240254)
@@ -1,3 +1,86 @@
+2019-01-22  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::finishCreation):
+        (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-21  Yusuke Suzuki  <ysuz...@apple.com>
 
         Unreviewed, roll out r240220 due to date-format-xparb regression

Modified: trunk/Source/_javascript_Core/bytecode/BytecodeList.rb (240253 => 240254)


--- trunk/Source/_javascript_Core/bytecode/BytecodeList.rb	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeList.rb	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2019-01-22 17:48:08 UTC (rev 240254)
@@ -626,7 +626,7 @@
             } else if (JSScope* constantScope = JSScope::constantScopeForCodeBlock(op.type, this)) {
                 metadata.m_constantScope.set(vm, this, constantScope);
                 if (op.type == GlobalLexicalVar || op.type == GlobalLexicalVarWithVarInjectionChecks)
-                    metadata.m_localScopeDepth = 0;
+                    metadata.m_globalLexicalBindingEpoch = m_globalObject->globalLexicalBindingEpoch();
             } else
                 metadata.m_globalObject = nullptr;
             break;
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/bytecode/CodeBlock.h	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.h	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2019-01-22 17:48:08 UTC (rev 240254)
@@ -6182,9 +6182,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);
                 }
             }
 
@@ -6296,8 +6297,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();
 
@@ -6471,8 +6474,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 (240253 => 240254)


--- trunk/Source/_javascript_Core/dfg/DFGDesiredGlobalProperties.cpp	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/dfg/DFGDesiredGlobalProperties.cpp	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/dfg/DFGDesiredGlobalProperties.h	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/dfg/DFGDesiredGlobalProperties.h	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/dfg/DFGGraph.cpp	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.cpp	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/dfg/DFGGraph.h	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/dfg/DFGPlan.cpp	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/dfg/DFGPlan.cpp	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/jit/JITPropertyAccess.cpp	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/jit/JITPropertyAccess.cpp	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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[t5], 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 (240253 => 240254)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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[t5], 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 (240253 => 240254)


--- trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/runtime/CommonSlowPaths.h	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/runtime/CommonSlowPaths.h	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2019-01-22 17:48:08 UTC (rev 240254)
@@ -1857,20 +1857,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)
@@ -1896,11 +1893,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 (240253 => 240254)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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 (240253 => 240254)


--- trunk/Source/_javascript_Core/runtime/Options.cpp	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/runtime/Options.cpp	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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)
@@ -566,6 +573,8 @@
 #if 0
                 ; // Deconfuse editors that do auto indentation
 #endif
+
+            correctOptions();
     
             recomputeDependentOptions();
 
@@ -699,6 +708,8 @@
         }
     }
 
+    correctOptions();
+
     recomputeDependentOptions();
 
     dumpOptionsIfNeeded();
@@ -735,6 +746,7 @@
         bool success = parse(valueStr, value);                     \
         if (success) {                                             \
             name_() = value;                                       \
+            correctOptions();                                      \
             recomputeDependentOptions();                           \
             return true;                                           \
         }                                                          \

Modified: trunk/Source/_javascript_Core/runtime/Options.h (240253 => 240254)


--- trunk/Source/_javascript_Core/runtime/Options.h	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/runtime/Options.h	2019-01-22 17:48:08 UTC (rev 240254)
@@ -508,8 +508,7 @@
     v(bool, traceLLIntExecution, false, Configurable, nullptr) \
     v(bool, traceLLIntSlowPath, false, Configurable, nullptr) \
     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 (240253 => 240254)


--- trunk/Source/_javascript_Core/runtime/ProgramExecutable.cpp	2019-01-22 17:25:59 UTC (rev 240253)
+++ trunk/Source/_javascript_Core/runtime/ProgramExecutable.cpp	2019-01-22 17:48:08 UTC (rev 240254)
@@ -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;
 }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to