WebKit Bugzilla
Attachment 358929 Details for
Bug 193308
: [JSC] Global lexical bindings can shadow global variables if it is `configurable = true`
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-193308-20190111123331.patch (text/plain), 83.70 KB, created by
Yusuke Suzuki
on 2019-01-11 12:33:32 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Yusuke Suzuki
Created:
2019-01-11 12:33:32 PST
Size:
83.70 KB
patch
obsolete
>Subversion Revision: 239777 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 263008d077553db015276c61364b89198e8e3552..eb8f72c4ab811c8f292ef258e12d01375d28d4c7 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,114 @@ >+2019-01-11 Yusuke Suzuki <yusukesuzuki@slowstart.org> >+ >+ [JSC] Global lexical bindings can shadow global variables if it is `configurable = true` >+ https://bugs.webkit.org/show_bug.cgi?id=193308 >+ <rdar://problem/45546542> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Previously, we assumed that lexical bindings in JSGlobalLexicalEnvironment cannot shadow existing global properties. >+ However, it is wrong. According to the spec, we can shadow global properties if a property's attribute is configurable = true. >+ For example, we execute two scripts. >+ >+ script1.js >+ >+ bar = 42; >+ function load() { return bar; } >+ print(bar); // 42 >+ print(load()); // 42 >+ >+ script2.js >+ >+ let bar = 0; // This lexical binding can shadow the global.bar defined in script1.js >+ print(bar); // 0 >+ print(load()); // 0 >+ >+ In JSC, we cache GlobalProperty resolve type and its associated information in op_resolve_type, op_get_from_scope, op_put_to_scope, and op_profile_type. >+ They attempt to load a property from JSGlobalObject directly. However, once the newly added lexical binding starts shadowing this, our existing instructions >+ become invalid since they do not respect JSGlobalLexicalEnvironment. >+ >+ In this patch, we fix this issue by introducing the following mechanisms. >+ >+ 1. We have a HashMap<property name, watchpoint set> in JSGlobalObject. DFG and FTL create a watchpoint set with the property name if the generated code >+ depends on GlobalProperty condition of op_resolve_scope etc. These watchpoint will be fired when the shadowing happens, so that our generated DFG and FTL >+ code will be invalidated if it depends on the condition which is no longer valid. >+ >+ 2. When we detect shadowing, we iterate all the live CodeBlocks which globalObject is the target one. And we rewrite instructions in them from GlobalProperty >+ to GlobalLexicalVar (or Dynamic precisely). So, the subsequent LLInt code just works well. >+ >+ 3. GlobalProperty scope operations in Baseline JIT start checking ResolveType in metadata, and emit code for GlobalProperty and GlobalLexicalVar. Once the rewrite >+ happens, baseline JIT continues working because it checks the rewritten metadata's ResolveType. >+ >+ We use this mechanism (which is similar to haveABadTime() thing) because, >+ >+ 1. Shadowing should be super rare. Before r214145, we made these cases as SytaxError. Thus, before r214145, this type of code cannot be executed in WebKit. >+ And the number of the live CodeBlocks for the given JSGlobalObject should be small. This supports introducing rather simple (but not so efficient) mechanism >+ instead of the complicated one. >+ >+ 2. Rewriting instructions immediately forces GlobalProperty => GlobalLexicalVar / Dynamic conversion in all the possible CodeBlock. This allows us to avoid >+ compilation failure loop in DFG and FTL: DFG and FTL codes are invalidated by the watchpoint, but we may attempt to compile the code with the invalidated watchpoint >+ and GlobalProperty status if we do not rewrite it. One possible other implementation is having and checking a counter in instruction, and every time we introduce >+ a new shadow binding, bump the counter. And eventually executed instruction will go to the slow path and rewrite itself. However, this way leaves the not-executed-again-yet >+ instructions as is, and DFG and FTL repeatedly fail to compile if we just watch the invalidated watchpoint for that. Rewriting all the existing GlobalProperty immediately >+ avoids this situation easily. >+ >+ * JavaScriptCore.xcodeproj/project.pbxproj: >+ * Sources.txt: >+ * bytecode/CodeBlock.cpp: >+ (JSC::CodeBlock::notifyLexicalBindingShadowing): >+ * bytecode/CodeBlock.h: >+ (JSC::CodeBlock::scriptMode const): >+ * bytecode/Watchpoint.h: >+ (JSC::WatchpointSet::create): >+ * dfg/DFGByteCodeParser.cpp: >+ (JSC::DFG::ByteCodeParser::parseBlock): >+ * dfg/DFGDesiredGlobalProperties.cpp: Added. >+ (JSC::DFG::DesiredGlobalProperties::syncInMainThreadForValidation): >+ (JSC::DFG::DesiredGlobalProperties::reallyAdd): >+ * dfg/DFGDesiredGlobalProperties.h: Added. >+ (JSC::DFG::DesiredGlobalProperties::addLazily): >+ We need this DesiredGlobalProperties mechanism since we do not want to ref() the UniquedStringImpl in DFG and FTL thread. >+ We keep JSGlobalObject* and identifierNumber, and materialize WatchpointSets for each JSGlobalObject's property referenced >+ from DFG and FTL and inject CodeBlock jettison watchpoints in the main thread. >+ * dfg/DFGDesiredGlobalProperty.h: Added. >+ (JSC::DFG::DesiredGlobalProperty::DesiredGlobalProperty): >+ (JSC::DFG::DesiredGlobalProperty::globalObject const): >+ (JSC::DFG::DesiredGlobalProperty::identifierNumber const): >+ (JSC::DFG::DesiredGlobalProperty::operator== const): >+ (JSC::DFG::DesiredGlobalProperty::operator!= const): >+ (JSC::DFG::DesiredGlobalProperty::isHashTableDeletedValue const): >+ (JSC::DFG::DesiredGlobalProperty::hash const): >+ (JSC::DFG::DesiredGlobalProperty::dumpInContext const): >+ (JSC::DFG::DesiredGlobalProperty::dump const): >+ (JSC::DFG::DesiredGlobalPropertyHash::hash): >+ (JSC::DFG::DesiredGlobalPropertyHash::equal): >+ * dfg/DFGGraph.h: >+ (JSC::DFG::Graph::globalProperties): >+ * dfg/DFGPlan.cpp: >+ (JSC::DFG::Plan::reallyAdd): >+ (JSC::DFG::Plan::syncInMainThreadForValidation): >+ (JSC::DFG::Plan::finalizeWithoutNotifyingCallback): >+ (JSC::DFG::Plan::cancel): >+ * dfg/DFGPlan.h: >+ (JSC::DFG::Plan::globalProperties): >+ * jit/JITPropertyAccess.cpp: >+ (JSC::JIT::emit_op_resolve_scope): >+ (JSC::JIT::emit_op_get_from_scope): >+ (JSC::JIT::emit_op_put_to_scope): >+ * jit/JITPropertyAccess32_64.cpp: >+ (JSC::JIT::emit_op_resolve_scope): >+ (JSC::JIT::emit_op_get_from_scope): >+ (JSC::JIT::emit_op_put_to_scope): >+ * runtime/JSGlobalObject.cpp: >+ (JSC::JSGlobalObject::addStaticGlobals): >+ (JSC::JSGlobalObject::notifyLexicalBindingShadowing): >+ (JSC::JSGlobalObject::getReferencedPropertyWatchpointSet): >+ (JSC::JSGlobalObject::ensureReferencedPropertyWatchpointSet): >+ * runtime/JSGlobalObject.h: >+ * runtime/ProgramExecutable.cpp: >+ (JSC::hasRestrictedGlobalProperty): >+ (JSC::ProgramExecutable::initializeGlobalProperties): >+ > 2019-01-08 Keith Miller <keith_miller@apple.com> > > builtins should be able to use async/await >diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >index 02c352f10a27082f58e338d96e5b2ec55e709f66..22163bfc68d61607a025cd7944d3572af3f239f9 100644 >--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >@@ -1084,6 +1084,7 @@ > 5DBB151B131D0B310056AD36 /* testapi.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 14D857740A4696C80032146C /* testapi.js */; }; > 5DBB1525131D0BD70056AD36 /* minidom.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 1412110D0A48788700480255 /* minidom.js */; }; > 5DE6E5B30E1728EC00180407 /* create_hash_table in Headers */ = {isa = PBXBuildFile; fileRef = F692A8540255597D01FF60F7 /* create_hash_table */; settings = {ATTRIBUTES = (); }; }; >+ 5E158AC350BC4EC7877DC0F4 /* ObjectPrototypeInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D0CC9E1CBC149AB8F403434 /* ObjectPrototypeInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 623A37EC1B87A7C000754209 /* RegisterMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 623A37EB1B87A7BD00754209 /* RegisterMap.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 627673241B680C1E00FD9F2E /* CallMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 627673221B680C1E00FD9F2E /* CallMode.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 62D2D3901ADF103F000206C1 /* FunctionRareData.h in Headers */ = {isa = PBXBuildFile; fileRef = 62D2D38E1ADF103F000206C1 /* FunctionRareData.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -1624,7 +1625,6 @@ > BC18C4440E16F5CD00B34460 /* NumberPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = BC2680C50E16D4E900A06E92 /* NumberPrototype.h */; settings = {ATTRIBUTES = (Private, ); }; }; > BC18C4450E16F5CD00B34460 /* ObjectConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = BC2680C70E16D4E900A06E92 /* ObjectConstructor.h */; settings = {ATTRIBUTES = (Private, ); }; }; > BC18C4460E16F5CD00B34460 /* ObjectPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = BC2680C90E16D4E900A06E92 /* ObjectPrototype.h */; settings = {ATTRIBUTES = (Private, ); }; }; >- 5E158AC350BC4EC7877DC0F4 /* ObjectPrototypeInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D0CC9E1CBC149AB8F403434 /* ObjectPrototypeInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; > BC18C4480E16F5CD00B34460 /* Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = F692A8780255597D01FF60F7 /* Operations.h */; settings = {ATTRIBUTES = (Private, ); }; }; > BC18C44B0E16F5CD00B34460 /* Parser.h in Headers */ = {isa = PBXBuildFile; fileRef = 93F0B3AA09BB4DC00068FCE3 /* Parser.h */; settings = {ATTRIBUTES = (Private, ); }; }; > BC18C4540E16F5CD00B34460 /* PropertyNameArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 65400C100A69BAF200509887 /* PropertyNameArray.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -1757,6 +1757,7 @@ > E3A0531C21342B680022EC14 /* WasmSectionParser.h in Headers */ = {isa = PBXBuildFile; fileRef = E3A0531821342B670022EC14 /* WasmSectionParser.h */; }; > E3A32BC71FC83147007D7E76 /* WeakMapImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = E3A32BC61FC8312E007D7E76 /* WeakMapImpl.h */; }; > E3A421431D6F58930007C617 /* PreciseJumpTargetsInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E3A421421D6F588F0007C617 /* PreciseJumpTargetsInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ E3BFA5D021E853A1009C0EBA /* DFGDesiredGlobalProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = E3BFA5CD21E853A1009C0EBA /* DFGDesiredGlobalProperty.h */; }; > E3BFD0BC1DAF808E0065DEA2 /* AccessCaseSnippetParams.h in Headers */ = {isa = PBXBuildFile; fileRef = E3BFD0BA1DAF807C0065DEA2 /* AccessCaseSnippetParams.h */; }; > E3C295DD1ED2CBDA00D3016F /* ObjectPropertyChangeAdaptiveWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = E3C295DC1ED2CBAA00D3016F /* ObjectPropertyChangeAdaptiveWatchpoint.h */; }; > E3C79CAB1DB9A4DC00D1ECA4 /* DOMJITEffect.h in Headers */ = {isa = PBXBuildFile; fileRef = E3C79CAA1DB9A4D600D1ECA4 /* DOMJITEffect.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -3669,6 +3670,7 @@ > 6A38CFA81E32B58B0060206F /* AsyncStackTrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AsyncStackTrace.h; sourceTree = "<group>"; }; > 6AD2CB4C19B9140100065719 /* DebuggerEvalEnabler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebuggerEvalEnabler.h; sourceTree = "<group>"; }; > 6BA93C9590484C5BAD9316EA /* JSScriptFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSScriptFetcher.h; sourceTree = "<group>"; }; >+ 6D0CC9E1CBC149AB8F403434 /* ObjectPrototypeInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectPrototypeInlines.h; sourceTree = "<group>"; }; > 70113D491A8DB093003848C4 /* IteratorOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IteratorOperations.cpp; sourceTree = "<group>"; }; > 70113D4A1A8DB093003848C4 /* IteratorOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IteratorOperations.h; sourceTree = "<group>"; }; > 7013CA891B491A9400CAE613 /* JSMicrotask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSMicrotask.cpp; sourceTree = "<group>"; }; >@@ -4487,7 +4489,6 @@ > BC2680C70E16D4E900A06E92 /* ObjectConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectConstructor.h; sourceTree = "<group>"; }; > BC2680C80E16D4E900A06E92 /* ObjectPrototype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectPrototype.cpp; sourceTree = "<group>"; }; > BC2680C90E16D4E900A06E92 /* ObjectPrototype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectPrototype.h; sourceTree = "<group>"; }; >- 6D0CC9E1CBC149AB8F403434 /* ObjectPrototypeInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectPrototypeInlines.h; sourceTree = "<group>"; }; > BC2680E60E16D52300A06E92 /* NumberConstructor.lut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NumberConstructor.lut.h; sourceTree = "<group>"; }; > BC3046060E1F497F003232CF /* Error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Error.h; sourceTree = "<group>"; }; > BC337BDE0E1AF0B80076918A /* GetterSetter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = GetterSetter.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; >@@ -4692,6 +4693,9 @@ > E3A32BC51FC8312D007D7E76 /* WeakMapImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WeakMapImpl.cpp; sourceTree = "<group>"; }; > E3A32BC61FC8312E007D7E76 /* WeakMapImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WeakMapImpl.h; sourceTree = "<group>"; }; > E3A421421D6F588F0007C617 /* PreciseJumpTargetsInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreciseJumpTargetsInlines.h; sourceTree = "<group>"; }; >+ E3BFA5CB21E853A0009C0EBA /* DFGDesiredGlobalProperties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGDesiredGlobalProperties.cpp; path = dfg/DFGDesiredGlobalProperties.cpp; sourceTree = "<group>"; }; >+ E3BFA5CC21E853A0009C0EBA /* DFGDesiredGlobalProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGDesiredGlobalProperties.h; path = dfg/DFGDesiredGlobalProperties.h; sourceTree = "<group>"; }; >+ E3BFA5CD21E853A1009C0EBA /* DFGDesiredGlobalProperty.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGDesiredGlobalProperty.h; path = dfg/DFGDesiredGlobalProperty.h; sourceTree = "<group>"; }; > E3BFD0B91DAF807C0065DEA2 /* AccessCaseSnippetParams.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AccessCaseSnippetParams.cpp; sourceTree = "<group>"; }; > E3BFD0BA1DAF807C0065DEA2 /* AccessCaseSnippetParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessCaseSnippetParams.h; sourceTree = "<group>"; }; > E3C295DC1ED2CBAA00D3016F /* ObjectPropertyChangeAdaptiveWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectPropertyChangeAdaptiveWatchpoint.h; sourceTree = "<group>"; }; >@@ -7337,6 +7341,9 @@ > 0FFFC94E14EF909500C72532 /* DFGCSEPhase.h */, > 0F2FC77016E12F6F0038D976 /* DFGDCEPhase.cpp */, > 0F2FC77116E12F6F0038D976 /* DFGDCEPhase.h */, >+ E3BFA5CB21E853A0009C0EBA /* DFGDesiredGlobalProperties.cpp */, >+ E3BFA5CC21E853A0009C0EBA /* DFGDesiredGlobalProperties.h */, >+ E3BFA5CD21E853A1009C0EBA /* DFGDesiredGlobalProperty.h */, > 0F8F2B97172F04FD007DBDA5 /* DFGDesiredIdentifiers.cpp */, > 0F8F2B98172F04FD007DBDA5 /* DFGDesiredIdentifiers.h */, > 0FFC92131B94E83E0071DD66 /* DFGDesiredInferredType.h */, >@@ -8724,6 +8731,7 @@ > A7D89CF617A0B8CC00773AD8 /* DFGCriticalEdgeBreakingPhase.h in Headers */, > 0FFFC95A14EF90A900C72532 /* DFGCSEPhase.h in Headers */, > 0F2FC77316E12F740038D976 /* DFGDCEPhase.h in Headers */, >+ E3BFA5D021E853A1009C0EBA /* DFGDesiredGlobalProperty.h in Headers */, > 0F8F2B9A172F0501007DBDA5 /* DFGDesiredIdentifiers.h in Headers */, > 0FFC92141B94E83E0071DD66 /* DFGDesiredInferredType.h in Headers */, > C2C0F7CE17BBFC5B00464FE4 /* DFGDesiredTransitions.h in Headers */, >diff --git a/Source/JavaScriptCore/Sources.txt b/Source/JavaScriptCore/Sources.txt >index cba4c4ba6004f930d5ca8563310a6243e36449e5..a2332b75776cfbda32793ba8e0c23970a01cbbae 100644 >--- a/Source/JavaScriptCore/Sources.txt >+++ b/Source/JavaScriptCore/Sources.txt >@@ -321,6 +321,7 @@ dfg/DFGConstantFoldingPhase.cpp > dfg/DFGConstantHoistingPhase.cpp > dfg/DFGCriticalEdgeBreakingPhase.cpp > dfg/DFGDCEPhase.cpp >+dfg/DFGDesiredGlobalProperties.cpp > dfg/DFGDesiredIdentifiers.cpp > dfg/DFGDesiredTransitions.cpp > dfg/DFGDesiredWatchpoints.cpp >diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >index 86db8693f60e773b61ba37358ca3ee937dc0f35d..44984da0ad6e7f713b75dbc5b3748f1c04b0d2f4 100644 >--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp >+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >@@ -107,6 +107,9 @@ > #endif > > namespace JSC { >+namespace CodeBlockInternal { >+static constexpr bool verbose = false; >+} // namespace CodeBlockInternal > > const ClassInfo CodeBlock::s_info = { > "CodeBlock", nullptr, nullptr, nullptr, >@@ -2663,6 +2666,99 @@ void CodeBlock::tallyFrequentExitSites() > } > #endif // ENABLE(DFG_JIT) > >+void CodeBlock::notifyLexicalBindingShadowing(VM& vm, const IdentifierSet& set) >+{ >+ // FIXME: Currently, module code do not query to JSGlobalLexicalEnvironment. So this case should be removed once it is fixed. >+ // https://bugs.webkit.org/show_bug.cgi?id=193347 >+ if (scriptMode() == JSParserScriptMode::Module) >+ return; >+ JSGlobalObject* globalObject = m_globalObject.get(); >+ >+ auto throwScope = DECLARE_THROW_SCOPE(vm); >+ >+ ConcurrentJSLocker locker(m_lock); >+ >+ for (const auto& instruction : *m_instructions) { >+ OpcodeID opcodeID = instruction->opcodeID(); >+ switch (opcodeID) { >+ case op_resolve_scope: { >+ auto bytecode = instruction->as<OpResolveScope>(); >+ auto& metadata = bytecode.metadata(this); >+ ResolveType originalResolveType = metadata.resolveType; >+ if (originalResolveType == GlobalProperty || originalResolveType == GlobalPropertyWithVarInjectionChecks) { >+ const Identifier& ident = identifier(bytecode.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.localScopeDepth, globalObject->globalScope(), ident, Get, bytecode.resolveType, InitializationMode::NotInitialization); >+ EXCEPTION_ASSERT_UNUSED(throwScope, !throwScope.exception()); >+ ASSERT(op.type == GlobalLexicalVarWithVarInjectionChecks || op.type == GlobalLexicalVar); >+ // FIXME: Need to add injection check here if necessary. >+ metadata.resolveType = needsVarInjectionChecks(originalResolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar; >+ metadata.localScopeDepth = op.depth; >+ ASSERT(!op.lexicalEnvironment); >+ JSScope* constantScope = JSScope::constantScopeForCodeBlock(metadata.resolveType, this); >+ ASSERT(constantScope == globalObject->globalScope()); >+ metadata.constantScope.set(vm, this, constantScope); >+ dataLogLnIf(CodeBlockInternal::verbose, "Rewrite op_resolve_scope from ", originalResolveType, " to ", metadata.resolveType); >+ } >+ } >+ break; >+ } >+ >+ case op_get_from_scope: { >+ auto bytecode = instruction->as<OpGetFromScope>(); >+ auto& metadata = bytecode.metadata(this); >+ ResolveType originalResolveType = metadata.getPutInfo.resolveType(); >+ if (originalResolveType == GlobalProperty || originalResolveType == GlobalPropertyWithVarInjectionChecks) { >+ const Identifier& ident = identifier(bytecode.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.localScopeDepth, globalObject->globalScope(), ident, Get, bytecode.getPutInfo.resolveType(), InitializationMode::NotInitialization); >+ EXCEPTION_ASSERT_UNUSED(throwScope, !throwScope.exception()); >+ ASSERT(op.type == GlobalLexicalVarWithVarInjectionChecks || op.type == GlobalLexicalVar); >+ metadata.getPutInfo = GetPutInfo(bytecode.getPutInfo.resolveMode(), needsVarInjectionChecks(originalResolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar, bytecode.getPutInfo.initializationMode()); >+ metadata.watchpointSet = op.watchpointSet; >+ metadata.operand = op.operand; >+ dataLogLnIf(CodeBlockInternal::verbose, "Rewrite op_get_from_scope from ", originalResolveType, " to ", metadata.getPutInfo.resolveType()); >+ } >+ } >+ break; >+ } >+ >+ case op_put_to_scope: { >+ auto bytecode = instruction->as<OpPutToScope>(); >+ auto& metadata = bytecode.metadata(this); >+ ResolveType originalResolveType = metadata.getPutInfo.resolveType(); >+ if (originalResolveType == GlobalProperty || originalResolveType == GlobalPropertyWithVarInjectionChecks) { >+ const Identifier& ident = identifier(bytecode.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.symbolTableOrScopeDepth, globalObject->globalScope(), ident, Put, bytecode.getPutInfo.resolveType(), bytecode.getPutInfo.initializationMode()); >+ EXCEPTION_ASSERT_UNUSED(throwScope, !throwScope.exception()); >+ ASSERT(op.type == GlobalLexicalVarWithVarInjectionChecks || op.type == GlobalLexicalVar || op.type == Dynamic); >+ >+ ResolveType resolveType = op.type; >+ if (resolveType == GlobalLexicalVarWithVarInjectionChecks || resolveType == GlobalLexicalVar) { >+ resolveType = needsVarInjectionChecks(originalResolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar; >+ metadata.watchpointSet = op.watchpointSet; >+ } >+ metadata.getPutInfo = GetPutInfo(bytecode.getPutInfo.resolveMode(), resolveType, bytecode.getPutInfo.initializationMode()); >+ metadata.operand = op.operand; >+ dataLogLnIf(CodeBlockInternal::verbose, "Rewrite op_put_to_scope from ", originalResolveType, " to ", metadata.getPutInfo.resolveType()); >+ } >+ } >+ break; >+ } >+ >+ default: >+ break; >+ } >+ } >+} >+ > #if ENABLE(VERBOSE_VALUE_PROFILE) > void CodeBlock::dumpValueProfiles() > { >diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.h b/Source/JavaScriptCore/bytecode/CodeBlock.h >index 6c766a8ab229db0f683e66c06b7d5c50b8e81217..a6926fdf97eb4e6c6fcd0eb276f805319ba0bfe4 100644 >--- a/Source/JavaScriptCore/bytecode/CodeBlock.h >+++ b/Source/JavaScriptCore/bytecode/CodeBlock.h >@@ -195,6 +195,8 @@ class CodeBlock : public JSCell { > void visitChildren(SlotVisitor&); > void finalizeUnconditionally(VM&); > >+ void notifyLexicalBindingShadowing(VM&, const IdentifierSet&); >+ > void dumpSource(); > void dumpSource(PrintStream&); > >@@ -212,6 +214,8 @@ class CodeBlock : public JSCell { > bool isStrictMode() const { return m_isStrictMode; } > ECMAMode ecmaMode() const { return isStrictMode() ? StrictMode : NotStrictMode; } > >+ JSParserScriptMode scriptMode() const { return m_unlinkedCode->scriptMode(); } >+ > bool hasInstalledVMTrapBreakpoints() const; > bool installVMTrapBreakpoints(); > >diff --git a/Source/JavaScriptCore/bytecode/Watchpoint.h b/Source/JavaScriptCore/bytecode/Watchpoint.h >index 18d8c4ec0ad564ed6ef4aa48c13d4012a65da623..3720cc71ffd9c790b22fb3364aecd66ad7669eaa 100644 >--- a/Source/JavaScriptCore/bytecode/Watchpoint.h >+++ b/Source/JavaScriptCore/bytecode/Watchpoint.h >@@ -124,6 +124,11 @@ class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> { > // FIXME: In many cases, it would be amazing if this *did* fire the watchpoints. I suspect that > // this might be hard to get right, but still, it might be awesome. > JS_EXPORT_PRIVATE ~WatchpointSet(); // Note that this will not fire any of the watchpoints; if you need to know when a WatchpointSet dies then you need a separate mechanism for this. >+ >+ static Ref<WatchpointSet> create(WatchpointState state) >+ { >+ return adoptRef(*new WatchpointSet(state)); >+ } > > // Fast way of getting the state, which only works from the main thread. > WatchpointState stateOnJSThread() const >diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >index a6d3128f755ce8748a249bebb91f990000ecb0f5..ad578a2ac7e336640773a12350c229af4a9d6d97 100644 >--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >@@ -6140,28 +6140,67 @@ void ByteCodeParser::parseBlock(unsigned limit) > case op_resolve_scope: { > auto bytecode = currentInstruction->as<OpResolveScope>(); > auto& metadata = bytecode.metadata(codeBlock); >- unsigned depth = metadata.localScopeDepth; > >- if (needsDynamicLookup(metadata.resolveType, op_resolve_scope)) { >+ ResolveType resolveType; >+ unsigned depth; >+ JSScope* constantScope = nullptr; >+ JSCell* lexicalEnvironment = nullptr; >+ SymbolTable* symbolTable = nullptr; >+ { >+ ConcurrentJSLocker locker(m_inlineStackTop->m_profiledBlock->m_lock); >+ resolveType = metadata.resolveType; >+ depth = metadata.localScopeDepth; >+ switch (resolveType) { >+ case GlobalProperty: >+ case GlobalVar: >+ case GlobalPropertyWithVarInjectionChecks: >+ case GlobalVarWithVarInjectionChecks: >+ case GlobalLexicalVar: >+ case GlobalLexicalVarWithVarInjectionChecks: >+ constantScope = metadata.constantScope.get(); >+ break; >+ case ModuleVar: >+ lexicalEnvironment = metadata.lexicalEnvironment.get(); >+ break; >+ case LocalClosureVar: >+ case ClosureVar: >+ case ClosureVarWithVarInjectionChecks: >+ symbolTable = metadata.symbolTable.get(); >+ break; >+ default: >+ break; >+ } >+ } >+ >+ if (needsDynamicLookup(resolveType, op_resolve_scope)) { > unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[bytecode.var]; > set(bytecode.dst, addToGraph(ResolveScope, OpInfo(identifierNumber), get(bytecode.scope))); > NEXT_OPCODE(op_resolve_scope); > } > > // get_from_scope and put_to_scope depend on this watchpoint forcing OSR exit, so they don't add their own watchpoints. >- if (needsVarInjectionChecks(metadata.resolveType)) >+ if (needsVarInjectionChecks(resolveType)) > m_graph.watchpoints().addLazily(m_inlineStackTop->m_codeBlock->globalObject()->varInjectionWatchpoint()); > >- switch (metadata.resolveType) { >+ // 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) { >+ if (resolveType == GlobalProperty || resolveType == GlobalPropertyWithVarInjectionChecks) { >+ unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[bytecode.var]; >+ JSGlobalObject* globalObject = m_inlineStackTop->m_codeBlock->globalObject(); >+ m_graph.globalProperties().addLazily(DesiredGlobalProperty(globalObject, identifierNumber)); >+ } >+ } >+ >+ switch (resolveType) { > case GlobalProperty: > case GlobalVar: > case GlobalPropertyWithVarInjectionChecks: > case GlobalVarWithVarInjectionChecks: > case GlobalLexicalVar: > case GlobalLexicalVarWithVarInjectionChecks: { >- JSScope* constantScope = JSScope::constantScopeForCodeBlock(metadata.resolveType, m_inlineStackTop->m_codeBlock); > RELEASE_ASSERT(constantScope); >- RELEASE_ASSERT(metadata.constantScope.get() == constantScope); >+ RELEASE_ASSERT(constantScope == JSScope::constantScopeForCodeBlock(resolveType, m_inlineStackTop->m_codeBlock)); > set(bytecode.dst, weakJSConstant(constantScope)); > addToGraph(Phantom, get(bytecode.scope)); > break; >@@ -6170,7 +6209,7 @@ void ByteCodeParser::parseBlock(unsigned limit) > // Since the value of the "scope" virtual register is not used in LLInt / baseline op_resolve_scope with ModuleVar, > // we need not to keep it alive by the Phantom node. > // Module environment is already strongly referenced by the CodeBlock. >- set(bytecode.dst, weakJSConstant(metadata.lexicalEnvironment.get())); >+ set(bytecode.dst, weakJSConstant(lexicalEnvironment)); > break; > } > case LocalClosureVar: >@@ -6181,7 +6220,7 @@ void ByteCodeParser::parseBlock(unsigned limit) > > // We have various forms of constant folding here. This is necessary to avoid > // spurious recompiles in dead-but-foldable code. >- if (SymbolTable* symbolTable = metadata.symbolTable.get()) { >+ if (symbolTable) { > InferredValue* singleton = symbolTable->singletonScope(); > if (JSValue value = singleton->inferredValue()) { > m_graph.watchpoints().addLazily(singleton); >@@ -6227,13 +6266,16 @@ void ByteCodeParser::parseBlock(unsigned limit) > auto& metadata = bytecode.metadata(codeBlock); > unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[bytecode.var]; > UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber]; >- ResolveType resolveType = metadata.getPutInfo.resolveType(); > >+ ResolveType resolveType; >+ GetPutInfo getPutInfo(0); > Structure* structure = 0; > WatchpointSet* watchpoints = 0; > uintptr_t operand; > { > ConcurrentJSLocker locker(m_inlineStackTop->m_profiledBlock->m_lock); >+ getPutInfo = metadata.getPutInfo; >+ resolveType = getPutInfo.resolveType(); > if (resolveType == GlobalVar || resolveType == GlobalVarWithVarInjectionChecks || resolveType == GlobalLexicalVar || resolveType == GlobalLexicalVarWithVarInjectionChecks) > watchpoints = metadata.watchpointSet; > else if (resolveType != UnresolvedProperty && resolveType != UnresolvedPropertyWithVarInjectionChecks) >@@ -6242,7 +6284,7 @@ void ByteCodeParser::parseBlock(unsigned limit) > } > > if (needsDynamicLookup(resolveType, op_get_from_scope)) { >- uint64_t opInfo1 = makeDynamicVarOpInfo(identifierNumber, metadata.getPutInfo.operand()); >+ uint64_t opInfo1 = makeDynamicVarOpInfo(identifierNumber, getPutInfo.operand()); > SpeculatedType prediction = getPrediction(); > set(bytecode.dst, > addToGraph(GetDynamicVar, OpInfo(opInfo1), OpInfo(prediction), get(bytecode.scope))); >@@ -6392,18 +6434,21 @@ void ByteCodeParser::parseBlock(unsigned limit) > unsigned identifierNumber = bytecode.var; > if (identifierNumber != UINT_MAX) > identifierNumber = m_inlineStackTop->m_identifierRemap[identifierNumber]; >- ResolveType resolveType = metadata.getPutInfo.resolveType(); > UniquedStringImpl* uid; > if (identifierNumber != UINT_MAX) > uid = m_graph.identifiers()[identifierNumber]; > else > uid = nullptr; > >+ ResolveType resolveType; >+ GetPutInfo getPutInfo(0); > Structure* structure = nullptr; > WatchpointSet* watchpoints = nullptr; > uintptr_t operand; > { > ConcurrentJSLocker locker(m_inlineStackTop->m_profiledBlock->m_lock); >+ getPutInfo = metadata.getPutInfo; >+ resolveType = getPutInfo.resolveType(); > if (resolveType == GlobalVar || resolveType == GlobalVarWithVarInjectionChecks || resolveType == LocalClosureVar || resolveType == GlobalLexicalVar || resolveType == GlobalLexicalVarWithVarInjectionChecks) > watchpoints = metadata.watchpointSet; > else if (resolveType != UnresolvedProperty && resolveType != UnresolvedPropertyWithVarInjectionChecks) >@@ -6415,7 +6460,7 @@ void ByteCodeParser::parseBlock(unsigned limit) > > if (needsDynamicLookup(resolveType, op_put_to_scope)) { > ASSERT(identifierNumber != UINT_MAX); >- uint64_t opInfo1 = makeDynamicVarOpInfo(identifierNumber, metadata.getPutInfo.operand()); >+ uint64_t opInfo1 = makeDynamicVarOpInfo(identifierNumber, getPutInfo.operand()); > addToGraph(PutDynamicVar, OpInfo(opInfo1), OpInfo(), get(bytecode.scope), get(bytecode.value)); > NEXT_OPCODE(op_put_to_scope); > } >@@ -6444,7 +6489,7 @@ void ByteCodeParser::parseBlock(unsigned limit) > case GlobalLexicalVarWithVarInjectionChecks: > case GlobalVar: > case GlobalVarWithVarInjectionChecks: { >- if (!isInitialization(metadata.getPutInfo.initializationMode()) && (resolveType == GlobalLexicalVar || resolveType == GlobalLexicalVarWithVarInjectionChecks)) { >+ if (!isInitialization(getPutInfo.initializationMode()) && (resolveType == GlobalLexicalVar || resolveType == GlobalLexicalVarWithVarInjectionChecks)) { > SpeculatedType prediction = SpecEmpty; > Node* value = addToGraph(GetGlobalLexicalVariable, OpInfo(operand), OpInfo(prediction)); > addToGraph(CheckNotEmpty, value); >diff --git a/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.cpp b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..36d574af1b30dba20cb604062c3667b5a359c1d1 >--- /dev/null >+++ b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.cpp >@@ -0,0 +1,64 @@ >+/* >+ * Copyright (C) 2019 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "DFGDesiredGlobalProperties.h" >+ >+#if ENABLE(DFG_JIT) >+ >+#include "CodeBlock.h" >+#include "DFGCommonData.h" >+#include "DFGDesiredIdentifiers.h" >+#include "JSCInlines.h" >+#include "JSGlobalObject.h" >+ >+namespace JSC { namespace DFG { >+ >+bool DesiredGlobalProperties::syncInMainThreadForValidation(DesiredIdentifiers& identifiers) >+{ >+ for (const auto& property : m_set) { >+ auto* uid = identifiers.at(property.identifierNumber()); >+ if (auto* watchpointSet = property.globalObject()->getReferencedPropertyWatchpointSet(uid)) { >+ if (!watchpointSet->isStillValid()) >+ return false; >+ } >+ } >+ return true; >+} >+ >+void DesiredGlobalProperties::reallyAdd(CodeBlock* codeBlock, DesiredIdentifiers& identifiers, CommonData& common) >+{ >+ for (const auto& property : m_set) { >+ auto* uid = identifiers.at(property.identifierNumber()); >+ auto& watchpointSet = property.globalObject()->ensureReferencedPropertyWatchpointSet(uid); >+ ASSERT(watchpointSet.isStillValid()); >+ watchpointSet.add(common.watchpoints.add(codeBlock)); >+ } >+} >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >+ >diff --git a/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.h b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.h >new file mode 100644 >index 0000000000000000000000000000000000000000..a50622e3fe8d12eac7add8082e3f5750fc0866d6 >--- /dev/null >+++ b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.h >@@ -0,0 +1,60 @@ >+/* >+ * Copyright (C) 2019 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#if ENABLE(DFG_JIT) >+ >+#include "DFGDesiredGlobalProperty.h" >+#include <wtf/HashMap.h> >+ >+namespace JSC { >+ >+class CodeBlock; >+class VM; >+ >+namespace DFG { >+ >+class CommonData; >+class DesiredIdentifiers; >+ >+class DesiredGlobalProperties { >+public: >+ void addLazily(DesiredGlobalProperty&& property) >+ { >+ m_set.add(WTFMove(property)); >+ } >+ >+ bool syncInMainThreadForValidation(DesiredIdentifiers&); >+ >+ void reallyAdd(CodeBlock*, DesiredIdentifiers&, CommonData&); >+ >+private: >+ HashSet<DesiredGlobalProperty> m_set; >+}; >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >diff --git a/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperty.h b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperty.h >new file mode 100644 >index 0000000000000000000000000000000000000000..b629a173cd8aff718ae47daf967c64d74c74cf99 >--- /dev/null >+++ b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperty.h >@@ -0,0 +1,114 @@ >+/* >+ * Copyright (C) 2019 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#if ENABLE(DFG_JIT) >+ >+#include "DumpContext.h" >+#include <wtf/HashMap.h> >+#include <wtf/PrintStream.h> >+ >+namespace JSC { >+ >+class JSGlobalObject; >+ >+namespace DFG { >+ >+class DesiredGlobalProperty { >+public: >+ DesiredGlobalProperty() = default; >+ >+ DesiredGlobalProperty(JSGlobalObject* globalObject, unsigned identifierNumber) >+ : m_globalObject(globalObject) >+ , m_identifierNumber(identifierNumber) >+ { >+ } >+ >+ DesiredGlobalProperty(WTF::HashTableDeletedValueType) >+ : m_globalObject(nullptr) >+ , m_identifierNumber(UINT_MAX) >+ { >+ } >+ >+ JSGlobalObject* globalObject() const { return m_globalObject; } >+ unsigned identifierNumber() const { return m_identifierNumber; } >+ >+ bool operator==(const DesiredGlobalProperty& other) const >+ { >+ return m_globalObject == other.m_globalObject && m_identifierNumber == other.m_identifierNumber; >+ } >+ >+ bool operator!=(const DesiredGlobalProperty& other) const >+ { >+ return !(*this == other); >+ } >+ >+ bool isHashTableDeletedValue() const >+ { >+ return !m_globalObject && m_identifierNumber == UINT_MAX; >+ } >+ >+ unsigned hash() const >+ { >+ return WTF::PtrHash<JSGlobalObject*>::hash(m_globalObject) + m_identifierNumber * 7; >+ } >+ >+ void dumpInContext(PrintStream& out, DumpContext*) const >+ { >+ out.print(m_identifierNumber, " for ", RawPointer(m_globalObject)); >+ } >+ >+ void dump(PrintStream& out) const >+ { >+ dumpInContext(out, nullptr); >+ } >+ >+private: >+ JSGlobalObject* m_globalObject { nullptr }; >+ unsigned m_identifierNumber { 0 }; >+}; >+ >+struct DesiredGlobalPropertyHash { >+ static unsigned hash(const DesiredGlobalProperty& key) { return key.hash(); } >+ static bool equal(const DesiredGlobalProperty& a, const DesiredGlobalProperty& b) { return a == b; } >+ static const bool safeToCompareToEmptyOrDeleted = true; >+}; >+ >+} } // namespace JSC::DFG >+ >+namespace WTF { >+ >+template<typename T> struct DefaultHash; >+template<> struct DefaultHash<JSC::DFG::DesiredGlobalProperty> { >+ typedef JSC::DFG::DesiredGlobalPropertyHash Hash; >+}; >+ >+template<typename T> struct HashTraits; >+template<> struct HashTraits<JSC::DFG::DesiredGlobalProperty> : SimpleClassHashTraits<JSC::DFG::DesiredGlobalProperty> { }; >+ >+} // namespace WTF >+ >+#endif // ENABLE(DFG_JIT) >diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h >index 74191d5bdc02ba0e2e7c9f2d8c231a4504f8d79a..2551f661904a8c41e0997563907215afd4017a0f 100644 >--- a/Source/JavaScriptCore/dfg/DFGGraph.h >+++ b/Source/JavaScriptCore/dfg/DFGGraph.h >@@ -785,6 +785,7 @@ class Graph : public virtual Scannable { > > DesiredIdentifiers& identifiers() { return m_plan.identifiers(); } > DesiredWatchpoints& watchpoints() { return m_plan.watchpoints(); } >+ DesiredGlobalProperties& globalProperties() { return m_plan.globalProperties(); } > > // Returns false if the key is already invalid or unwatchable. If this is a Presence condition, > // this also makes it cheap to query if the condition holds. Also makes sure that the GC knows >diff --git a/Source/JavaScriptCore/dfg/DFGPlan.cpp b/Source/JavaScriptCore/dfg/DFGPlan.cpp >index cde4860eccc75929e8a33e817095860783e11b17..52cbafc468edd7911c2557c4cf7bcaee4756689a 100644 >--- a/Source/JavaScriptCore/dfg/DFGPlan.cpp >+++ b/Source/JavaScriptCore/dfg/DFGPlan.cpp >@@ -556,6 +556,7 @@ void Plan::reallyAdd(CommonData* commonData) > m_identifiers.reallyAdd(*m_vm, commonData); > m_weakReferences.reallyAdd(*m_vm, commonData); > m_transitions.reallyAdd(*m_vm, commonData); >+ m_globalProperties.reallyAdd(m_codeBlock, m_identifiers, *commonData); > commonData->recordedStatuses = WTFMove(m_recordedStatuses); > } > >@@ -570,12 +571,17 @@ void Plan::notifyReady() > m_stage = Ready; > } > >+bool Plan::syncInMainThreadForValidation() >+{ >+ return m_globalProperties.syncInMainThreadForValidation(m_identifiers); >+} >+ > CompilationResult Plan::finalizeWithoutNotifyingCallback() > { > // We will establish new references from the code block to things. So, we need a barrier. > m_vm->heap.writeBarrier(m_codeBlock); > >- if (!isStillValid()) { >+ if (!syncInMainThreadForValidation() || !isStillValid()) { > CODEBLOCK_LOG_EVENT(m_codeBlock, "dfgFinalize", ("invalidated")); > return CompilationInvalidated; > } >@@ -682,6 +688,7 @@ void Plan::cancel() > m_inlineCallFrames = nullptr; > m_watchpoints = DesiredWatchpoints(); > m_identifiers = DesiredIdentifiers(); >+ m_globalProperties = DesiredGlobalProperties(); > m_weakReferences = DesiredWeakReferences(); > m_transitions = DesiredTransitions(); > m_callback = nullptr; >diff --git a/Source/JavaScriptCore/dfg/DFGPlan.h b/Source/JavaScriptCore/dfg/DFGPlan.h >index 3da11bd62b128c867ecd3ebf06a893a65e7e1f40..f7151bcf6f0cdb84defc58a5bd632c80a5ef14ce 100644 >--- a/Source/JavaScriptCore/dfg/DFGPlan.h >+++ b/Source/JavaScriptCore/dfg/DFGPlan.h >@@ -28,6 +28,7 @@ > #include "CompilationResult.h" > #include "DFGCompilationKey.h" > #include "DFGCompilationMode.h" >+#include "DFGDesiredGlobalProperties.h" > #include "DFGDesiredIdentifiers.h" > #include "DFGDesiredTransitions.h" > #include "DFGDesiredWatchpoints.h" >@@ -101,6 +102,7 @@ class Plan : public ThreadSafeRefCounted<Plan> { > DesiredIdentifiers& identifiers() { return m_identifiers; } > DesiredWeakReferences& weakReferences() { return m_weakReferences; } > DesiredTransitions& transitions() { return m_transitions; } >+ DesiredGlobalProperties& globalProperties() { return m_globalProperties; } > RecordedStatuses& recordedStatuses() { return m_recordedStatuses; } > > bool willTryToTierUp() const { return m_willTryToTierUp; } >@@ -122,6 +124,7 @@ class Plan : public ThreadSafeRefCounted<Plan> { > enum CompilationPath { FailPath, DFGPath, FTLPath, CancelPath }; > CompilationPath compileInThreadImpl(); > >+ bool syncInMainThreadForValidation(); > bool isStillValid(); > void reallyAdd(CommonData*); > >@@ -151,6 +154,7 @@ class Plan : public ThreadSafeRefCounted<Plan> { > DesiredIdentifiers m_identifiers; > DesiredWeakReferences m_weakReferences; > DesiredTransitions m_transitions; >+ DesiredGlobalProperties m_globalProperties; > RecordedStatuses m_recordedStatuses; > > bool m_willTryToTierUp { false }; >diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp >index ad1d3dccc62ba5f7382702c2ee9e1e115d34f3d9..ef74836aa1c5c5f6e4afae674665542b4191d016 100644 >--- a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp >+++ b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp >@@ -766,9 +766,12 @@ void JIT::emit_op_resolve_scope(const Instruction* currentInstruction) > > auto emitCode = [&] (ResolveType resolveType) { > switch (resolveType) { >+ // Here, we get constant value for GlobalProperty and GlobalPropertyWithVarInjectionChecks. >+ // This is because UnresolvedProperty already has a resolveType check. If a resolveType is changed >+ // to GlobalLexicalVar, we do not enter the code for GlobalProperty. > case GlobalProperty: >- case GlobalVar: > case GlobalPropertyWithVarInjectionChecks: >+ case GlobalVar: > case GlobalVarWithVarInjectionChecks: > case GlobalLexicalVar: > case GlobalLexicalVarWithVarInjectionChecks: { >@@ -798,6 +801,15 @@ void JIT::emit_op_resolve_scope(const Instruction* currentInstruction) > }; > > switch (resolveType) { >+ case GlobalProperty: >+ case GlobalPropertyWithVarInjectionChecks: { >+ // Since these GlobalProperty can be changed to GlobalLexicalVar, we should load the value from metadata. >+ JSScope** constantScopeSlot = metadata.constantScope.slot(); >+ emitVarInjectionCheck(needsVarInjectionChecks(resolveType)); >+ loadPtr(constantScopeSlot, regT0); >+ emitPutVirtualRegister(dst); >+ break; >+ } > case UnresolvedProperty: > case UnresolvedPropertyWithVarInjectionChecks: { > JumpList skipToEnd; >@@ -925,6 +937,22 @@ void JIT::emit_op_get_from_scope(const Instruction* currentInstruction) > }; > > switch (resolveType) { >+ case GlobalProperty: >+ case GlobalPropertyWithVarInjectionChecks: { >+ JumpList skipToEnd; >+ load32(&metadata.getPutInfo, regT0); >+ and32(TrustedImm32(GetPutInfo::typeBits), regT0); // Load ResolveType into T0 >+ >+ Jump isNotGlobalProperty = branch32(NotEqual, regT0, TrustedImm32(resolveType)); >+ emitCode(resolveType, false); >+ skipToEnd.append(jump()); >+ >+ isNotGlobalProperty.link(this); >+ emitCode(needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar, true); >+ >+ skipToEnd.link(this); >+ break; >+ } > case UnresolvedProperty: > case UnresolvedPropertyWithVarInjectionChecks: { > JumpList skipToEnd; >@@ -1064,6 +1092,25 @@ void JIT::emit_op_put_to_scope(const Instruction* currentInstruction) > }; > > switch (resolveType) { >+ case GlobalProperty: >+ case GlobalPropertyWithVarInjectionChecks: { >+ JumpList skipToEnd; >+ load32(&metadata.getPutInfo, regT0); >+ and32(TrustedImm32(GetPutInfo::typeBits), regT0); // Load ResolveType into T0 >+ >+ Jump isGlobalProperty = branch32(Equal, regT0, TrustedImm32(resolveType)); >+ Jump isGlobalLexicalVar = branch32(Equal, regT0, TrustedImm32(needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar)); >+ addSlowCase(jump()); // Dynamic, it can happen if we attempt to put a value to already-initialized const binding. >+ >+ isGlobalLexicalVar.link(this); >+ emitCode(needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar, true); >+ skipToEnd.append(jump()); >+ >+ isGlobalProperty.link(this); >+ emitCode(resolveType, false); >+ skipToEnd.link(this); >+ break; >+ } > case UnresolvedProperty: > case UnresolvedPropertyWithVarInjectionChecks: { > JumpList skipToEnd; >diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp >index e8314ca79d1939ba3eb955000b73d952c26d3e2b..dd43b5b9d2accffc881ba1155c75444df229ec7f 100644 >--- a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp >+++ b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp >@@ -769,11 +769,14 @@ void JIT::emit_op_resolve_scope(const Instruction* currentInstruction) > > auto emitCode = [&] (ResolveType resolveType) { > switch (resolveType) { >+ // Here, we get constant value for GlobalProperty and GlobalPropertyWithVarInjectionChecks. >+ // This is because UnresolvedProperty already has a resolveType check. If a resolveType is changed >+ // to GlobalLexicalVar, we do not enter the code for GlobalProperty. > case GlobalProperty: >- case GlobalVar: >- case GlobalLexicalVar: > case GlobalPropertyWithVarInjectionChecks: >+ case GlobalVar: > case GlobalVarWithVarInjectionChecks: >+ case GlobalLexicalVar: > case GlobalLexicalVarWithVarInjectionChecks: { > JSScope* constantScope = JSScope::constantScopeForCodeBlock(resolveType, m_codeBlock); > RELEASE_ASSERT(constantScope); >@@ -802,6 +805,16 @@ void JIT::emit_op_resolve_scope(const Instruction* currentInstruction) > } > }; > switch (resolveType) { >+ case GlobalProperty: >+ case GlobalPropertyWithVarInjectionChecks: { >+ // Since these GlobalProperty can be changed to GlobalLexicalVar, we should load the value from metadata. >+ JSScope** constantScopeSlot = metadata.constantScope.slot(); >+ emitVarInjectionCheck(needsVarInjectionChecks(resolveType)); >+ move(TrustedImm32(JSValue::CellTag), regT1); >+ loadPtr(constantScopeSlot, regT0); >+ emitStore(dst, regT1, regT0); >+ break; >+ } > case UnresolvedProperty: > case UnresolvedPropertyWithVarInjectionChecks: { > JumpList skipToEnd; >@@ -927,6 +940,21 @@ void JIT::emit_op_get_from_scope(const Instruction* currentInstruction) > }; > > switch (resolveType) { >+ case GlobalProperty: >+ case GlobalPropertyWithVarInjectionChecks: { >+ JumpList skipToEnd; >+ load32(¤tInstruction[4], regT0); >+ and32(TrustedImm32(GetPutInfo::typeBits), regT0); // Load ResolveType into T0 >+ >+ Jump isNotGlobalProperty = branch32(NotEqual, regT0, TrustedImm32(resolveType)); >+ emitCode(resolveType, false); >+ skipToEnd.append(jump()); >+ >+ isNotGlobalProperty.link(this); >+ emitCode(needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar, true); >+ skipToEnd.link(this); >+ break; >+ } > case UnresolvedProperty: > case UnresolvedPropertyWithVarInjectionChecks: { > JumpList skipToEnd; >@@ -1067,6 +1095,25 @@ void JIT::emit_op_put_to_scope(const Instruction* currentInstruction) > }; > > switch (resolveType) { >+ case GlobalProperty: >+ case GlobalPropertyWithVarInjectionChecks: { >+ JumpList skipToEnd; >+ load32(¤tInstruction[4], regT0); >+ and32(TrustedImm32(GetPutInfo::typeBits), regT0); // Load ResolveType into T0 >+ >+ Jump isGlobalProperty = branch32(Equal, regT0, TrustedImm32(resolveType)); >+ Jump isGlobalLexicalVar = branch32(Equal, regT0, TrustedImm32(needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar)); >+ addSlowCase(jump()); // Dynamic, it can happen if we attempt to put a value to already-initialized const binding. >+ >+ isGlobalLexicalVar.link(this); >+ emitCode(needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar, true); >+ skipToEnd.append(jump()); >+ >+ isGlobalProperty.link(this); >+ emitCode(resolveType, false); >+ skipToEnd.link(this); >+ break; >+ } > case UnresolvedProperty: > case UnresolvedPropertyWithVarInjectionChecks: { > JumpList skipToEnd; >diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >index e86e82cfc6621c2a3493079bf8a429e64f58510d..34d14b8d671a9de83b220ff0365e76176589264a 100644 >--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >@@ -50,6 +50,7 @@ > #include "CatchScope.h" > #include "ClonedArguments.h" > #include "CodeBlock.h" >+#include "CodeBlockSetInlines.h" > #include "CodeCache.h" > #include "ConsoleObject.h" > #include "DateConstructor.h" >@@ -1711,6 +1712,8 @@ void JSGlobalObject::addStaticGlobals(GlobalPropertyInfo* globals, int count) > > for (int i = 0; i < count; ++i) { > GlobalPropertyInfo& global = globals[i]; >+ // This `configurable = false` is necessary condition for static globals, >+ // otherwise lexical bindings can change the result of GlobalVar queries too. > ASSERT(global.attributes & PropertyAttribute::DontDelete); > > WatchpointSet* watchpointSet = nullptr; >@@ -1846,6 +1849,19 @@ const HashSet<String>& JSGlobalObject::intlPluralRulesAvailableLocales() > } > #endif // ENABLE(INTL) > >+void JSGlobalObject::notifyLexicalBindingShadowing(VM& vm, const IdentifierSet& set) >+{ >+#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); >+ }); >+} >+ > void JSGlobalObject::queueMicrotask(Ref<Microtask>&& task) > { > if (globalObjectMethodTable()->queueTaskToEventLoop) { >@@ -1866,6 +1882,20 @@ bool JSGlobalObject::hasInteractiveDebugger() const > return m_debugger && m_debugger->isInteractivelyDebugging(); > } > >+#if ENABLE(DFG_JIT) >+WatchpointSet* JSGlobalObject::getReferencedPropertyWatchpointSet(UniquedStringImpl* uid) >+{ >+ return m_referencedGlobalPropertyWatchpointSets.get(uid); >+} >+ >+WatchpointSet& JSGlobalObject::ensureReferencedPropertyWatchpointSet(UniquedStringImpl* uid) >+{ >+ return m_referencedGlobalPropertyWatchpointSets.ensure(uid, [] { >+ return WatchpointSet::create(IsWatched); >+ }).iterator->value.get(); >+} >+#endif >+ > JSGlobalObject* JSGlobalObject::create(VM& vm, Structure* structure) > { > JSGlobalObject* globalObject = new (NotNull, allocateCell<JSGlobalObject>(vm.heap)) JSGlobalObject(vm, structure); >diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h >index ef597fb7883f0aaf00b229f6628881469c043027..3a1382d57979cc29b16fb68e93f4fb7d97e6e39d 100644 >--- a/Source/JavaScriptCore/runtime/JSGlobalObject.h >+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h >@@ -483,6 +483,11 @@ class JSGlobalObject : public JSSegmentedVariableObject { > bool isMapPrototypeSetFastAndNonObservable(); > bool isSetPrototypeAddFastAndNonObservable(); > >+#if ENABLE(DFG_JIT) >+ using ReferencedGlobalPropertyWatchpointSets = HashMap<RefPtr<UniquedStringImpl>, Ref<WatchpointSet>, IdentifierRepHash>; >+ ReferencedGlobalPropertyWatchpointSets m_referencedGlobalPropertyWatchpointSets; >+#endif >+ > bool m_evalEnabled { true }; > bool m_webAssemblyEnabled { true }; > String m_evalDisabledErrorMessage; >@@ -516,6 +521,11 @@ class JSGlobalObject : public JSSegmentedVariableObject { > bool hasInteractiveDebugger() const; > const RuntimeFlags& runtimeFlags() const { return m_runtimeFlags; } > >+#if ENABLE(DFG_JIT) >+ WatchpointSet* getReferencedPropertyWatchpointSet(UniquedStringImpl*); >+ WatchpointSet& ensureReferencedPropertyWatchpointSet(UniquedStringImpl*); >+#endif >+ > protected: > JS_EXPORT_PRIVATE explicit JSGlobalObject(VM&, Structure*, const GlobalObjectMethodTable* = nullptr); > >@@ -741,6 +751,8 @@ class JSGlobalObject : public JSSegmentedVariableObject { > const HashSet<String>& intlPluralRulesAvailableLocales(); > #endif // ENABLE(INTL) > >+ void notifyLexicalBindingShadowing(VM&, const IdentifierSet&); >+ > void setConsoleClient(ConsoleClient* consoleClient) { m_consoleClient = consoleClient; } > ConsoleClient* consoleClient() const { return m_consoleClient; } > >diff --git a/Source/JavaScriptCore/runtime/ProgramExecutable.cpp b/Source/JavaScriptCore/runtime/ProgramExecutable.cpp >index 393b007aca8df1b4b01cf566243d6dfc759a9cfb..071bc57455644c3c9194205507ff9d79a1b8aec5 100644 >--- a/Source/JavaScriptCore/runtime/ProgramExecutable.cpp >+++ b/Source/JavaScriptCore/runtime/ProgramExecutable.cpp >@@ -59,14 +59,19 @@ void ProgramExecutable::destroy(JSCell* cell) > } > > // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasrestrictedglobalproperty >-static bool hasRestrictedGlobalProperty(ExecState* exec, JSGlobalObject* globalObject, PropertyName propertyName) >+enum class GlobalPropertyLookUpStatus { >+ NotFound, >+ Configurable, >+ NonConfigurable, >+}; >+static GlobalPropertyLookUpStatus hasRestrictedGlobalProperty(ExecState* exec, JSGlobalObject* globalObject, PropertyName propertyName) > { > PropertyDescriptor descriptor; > if (!globalObject->getOwnPropertyDescriptor(exec, propertyName, descriptor)) >- return false; >+ return GlobalPropertyLookUpStatus::NotFound; > if (descriptor.configurable()) >- return false; >- return true; >+ return GlobalPropertyLookUpStatus::Configurable; >+ return GlobalPropertyLookUpStatus::NonConfigurable; > } > > JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callFrame, JSScope* scope) >@@ -102,6 +107,7 @@ JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callF > JSGlobalLexicalEnvironment* globalLexicalEnvironment = globalObject->globalLexicalEnvironment(); > const VariableEnvironment& variableDeclarations = unlinkedCodeBlock->variableDeclarations(); > const VariableEnvironment& lexicalDeclarations = unlinkedCodeBlock->lexicalDeclarations(); >+ IdentifierSet shadowedProperties; > // The ES6 spec says that no vars/global properties/let/const can be duplicated in the global scope. > // This carried out section 15.1.8 of the ES6 spec: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-globaldeclarationinstantiation > { >@@ -112,16 +118,30 @@ JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callF > return createSyntaxError(exec, makeString("Can't create duplicate variable: '", String(entry.key.get()), "'")); > } > >- // Check if any new "let"/"const"/"class" will shadow any pre-existing global property names, or "var"/"let"/"const" variables. >+ // Check if any new "let"/"const"/"class" will shadow any pre-existing global property names (with configurable = false), or "var"/"let"/"const" variables. > // It's an error to introduce a shadow. > for (auto& entry : lexicalDeclarations) { > // The ES6 spec says that RestrictedGlobalProperty can't be shadowed. >- bool hasProperty = hasRestrictedGlobalProperty(exec, globalObject, entry.key.get()); >+ GlobalPropertyLookUpStatus status = hasRestrictedGlobalProperty(exec, globalObject, entry.key.get()); > RETURN_IF_EXCEPTION(throwScope, throwScope.exception()); >- if (hasProperty) >+ switch (status) { >+ case GlobalPropertyLookUpStatus::NonConfigurable: > return createSyntaxError(exec, makeString("Can't create duplicate variable that shadows a global property: '", String(entry.key.get()), "'")); >+ case GlobalPropertyLookUpStatus::Configurable: >+ // 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. >+ // 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; >+ } > >- hasProperty = globalLexicalEnvironment->hasProperty(exec, entry.key.get()); >+ bool hasProperty = globalLexicalEnvironment->hasProperty(exec, entry.key.get()); > RETURN_IF_EXCEPTION(throwScope, throwScope.exception()); > if (hasProperty) { > if (UNLIKELY(entry.value.isConst() && !vm.globalConstRedeclarationShouldThrow() && !isStrictMode())) { >@@ -186,6 +206,10 @@ JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callF > RELEASE_ASSERT(offsetForAssert == offset); > } > } >+ >+ if (!shadowedProperties.isEmpty()) >+ globalObject->notifyLexicalBindingShadowing(vm, WTFMove(shadowedProperties)); >+ > return nullptr; > } > >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index e5a31714c8cf048efb04058b479bda6c43494550..b132c5c3d67fb5b61e63469241688de769f2f2cb 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,67 @@ >+2019-01-11 Yusuke Suzuki <yusukesuzuki@slowstart.org> >+ >+ [JSC] Global lexical bindings can shadow global variables if it is `configurable = true` >+ https://bugs.webkit.org/show_bug.cgi?id=193308 >+ <rdar://problem/45546542> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * stress/const-lexical-binding-shadow-existing-global-property-ftl.js: Added. >+ (shouldThrow): >+ (shouldBe): >+ (foo): >+ (get shouldThrow): >+ * stress/const-lexical-binding-shadow-existing-global-property-tdz-ftl.js: Added. >+ (shouldThrow): >+ (shouldBe): >+ (foo): >+ (get shouldBe): >+ (get shouldThrow): >+ (get return): >+ * stress/const-lexical-binding-shadow-existing-global-property-tdz.js: Added. >+ (shouldThrow): >+ (shouldBe): >+ (foo): >+ (get shouldBe): >+ (get shouldThrow): >+ * stress/const-lexical-binding-shadow-existing-global-property.js: Added. >+ (shouldThrow): >+ (shouldBe): >+ (foo): >+ * stress/const-lexical-binding-shadowing-global-properties-and-eval-injection.js: Added. >+ (shouldThrow): >+ (shouldBe): >+ (foo): >+ * stress/global-add-function-should-not-be-shadowed-by-lexical-bindings.js: Added. >+ (shouldThrow): >+ * stress/global-static-variables-should-not-be-shadowed-by-lexical-bindings.js: Added. >+ (shouldThrow): >+ * stress/let-lexical-binding-shadow-existing-global-property-ftl.js: Added. >+ (shouldThrow): >+ (shouldBe): >+ (foo): >+ * stress/let-lexical-binding-shadow-existing-global-property-tdz-ftl.js: Added. >+ (shouldThrow): >+ (shouldBe): >+ (foo): >+ (get shouldBe): >+ (get shouldThrow): >+ (get return): >+ * stress/let-lexical-binding-shadow-existing-global-property-tdz.js: Added. >+ (shouldThrow): >+ (shouldBe): >+ (foo): >+ (get shouldBe): >+ (get shouldThrow): >+ * stress/let-lexical-binding-shadow-existing-global-property.js: Added. >+ (shouldThrow): >+ (shouldBe): >+ (foo): >+ * stress/let-lexical-binding-shadowing-global-properties-and-eval-injection.js: Added. >+ (shouldThrow): >+ (shouldBe): >+ (foo): >+ > 2019-01-08 Yusuke Suzuki <yusukesuzuki@slowstart.org> > > Array.prototype.flat/flatMap have a minor bug in ArraySpeciesCreate >diff --git a/JSTests/stress/const-lexical-binding-shadow-existing-global-property-ftl.js b/JSTests/stress/const-lexical-binding-shadow-existing-global-property-ftl.js >new file mode 100644 >index 0000000000000000000000000000000000000000..0d032b36de83a6fda7e3c2ea8d5e73e4de6533ee >--- /dev/null >+++ b/JSTests/stress/const-lexical-binding-shadow-existing-global-property-ftl.js >@@ -0,0 +1,49 @@ >+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 shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error(`bad value: ${String(actual)}`); >+} >+noInline(shouldBe); >+ >+function foo() { >+ bar = 4; >+} >+function get() { >+ return bar; >+} >+for (var i = 0; i < 1e6; ++i) >+ foo(); >+for (var i = 0; i < 1e6; ++i) >+ shouldBe(get(), 4); >+ >+shouldBe(bar, 4); >+$.evalScript('const bar = 3;'); >+shouldBe(bar, 3); >+shouldBe(get(), 3); >+ >+for (var i = 0; i < 1e6; ++i) >+ shouldBe(get(), 3); >+ >+shouldThrow(() => { >+ foo(); >+}, `TypeError: Attempted to assign to readonly property.`); >+shouldBe(bar, 3); >+shouldBe(get(), 3); >+ >+for (var i = 0; i < 1e6; ++i) >+ shouldBe(get(), 3); >diff --git a/JSTests/stress/const-lexical-binding-shadow-existing-global-property-tdz-ftl.js b/JSTests/stress/const-lexical-binding-shadow-existing-global-property-tdz-ftl.js >new file mode 100644 >index 0000000000000000000000000000000000000000..21d093158e5217e4469232fd21c8aafb428af284 >--- /dev/null >+++ b/JSTests/stress/const-lexical-binding-shadow-existing-global-property-tdz-ftl.js >@@ -0,0 +1,53 @@ >+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 shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error(`bad value: ${String(actual)}`); >+} >+noInline(shouldBe); >+ >+function foo() { >+ bar = 4; >+} >+function get() { >+ return bar; >+} >+for (var i = 0; i < 1e6; ++i) >+ foo(); >+for (var i = 0; i < 1e6; ++i) >+ shouldBe(get(), 4); >+ >+shouldBe(bar, 4); >+shouldThrow(() => { >+ $.evalScript('get(); const bar = 3;'); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ shouldBe(bar, 3); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ shouldBe(get(), 3); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ $.evalScript('bar;'); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+ >+for (var i = 0; i < 1e3; ++i) { >+ shouldThrow(() => { >+ shouldBe(get(), 3); >+ }, `ReferenceError: Cannot access uninitialized variable.`); >+} >+ >diff --git a/JSTests/stress/const-lexical-binding-shadow-existing-global-property-tdz.js b/JSTests/stress/const-lexical-binding-shadow-existing-global-property-tdz.js >new file mode 100644 >index 0000000000000000000000000000000000000000..c4e894412bc11e9c258dd992fd8fb9bfc6e71ef3 >--- /dev/null >+++ b/JSTests/stress/const-lexical-binding-shadow-existing-global-property-tdz.js >@@ -0,0 +1,44 @@ >+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 shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error(`bad value: ${String(actual)}`); >+} >+noInline(shouldBe); >+ >+function foo() { >+ bar = 4; >+} >+function get() { >+ return bar; >+} >+foo(); >+shouldBe(get(), 4); >+ >+shouldBe(bar, 4); >+shouldThrow(() => { >+ $.evalScript('get(); const bar = 3;'); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ shouldBe(bar, 3); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ shouldBe(get(), 3); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ $.evalScript('bar;'); >+}, `ReferenceError: Cannot access uninitialized variable.`); >diff --git a/JSTests/stress/const-lexical-binding-shadow-existing-global-property.js b/JSTests/stress/const-lexical-binding-shadow-existing-global-property.js >new file mode 100644 >index 0000000000000000000000000000000000000000..92b29de8860a9717bdfaa2c472257f2697b342f8 >--- /dev/null >+++ b/JSTests/stress/const-lexical-binding-shadow-existing-global-property.js >@@ -0,0 +1,33 @@ >+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 shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error(`bad value: ${String(actual)}`); >+} >+noInline(shouldBe); >+ >+function foo() { >+ bar = 4; >+} >+foo(); >+shouldBe(bar, 4); >+$.evalScript('const bar = 3;'); >+shouldBe(bar, 3); >+shouldThrow(() => { >+ foo(); >+}, `TypeError: Attempted to assign to readonly property.`); >+shouldBe(bar, 3); >diff --git a/JSTests/stress/const-lexical-binding-shadowing-global-properties-and-eval-injection.js b/JSTests/stress/const-lexical-binding-shadowing-global-properties-and-eval-injection.js >new file mode 100644 >index 0000000000000000000000000000000000000000..ddf3f944a214513ec09419f887d384cf8c65ad20 >--- /dev/null >+++ b/JSTests/stress/const-lexical-binding-shadowing-global-properties-and-eval-injection.js >@@ -0,0 +1,36 @@ >+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 shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error(`bad value: ${String(actual)}`); >+} >+noInline(shouldBe); >+ >+bar = 0; >+function foo(code) { >+ eval(code); >+ return (function () { >+ return bar; >+ }()); >+} >+shouldBe(foo(`42`), 0); >+ >+$.evalScript(`const bar = 42`); >+shouldBe(foo(`42`), 42); >+ >+shouldBe(foo(`var bar = 1`), 1); >+shouldBe(foo(`42`), 42); >diff --git a/JSTests/stress/global-add-function-should-not-be-shadowed-by-lexical-bindings.js b/JSTests/stress/global-add-function-should-not-be-shadowed-by-lexical-bindings.js >new file mode 100644 >index 0000000000000000000000000000000000000000..9ebb839606be2156151b8c6c7bbb1e54be7d1c63 >--- /dev/null >+++ b/JSTests/stress/global-add-function-should-not-be-shadowed-by-lexical-bindings.js >@@ -0,0 +1,18 @@ >+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)}`); >+} >+ >+shouldThrow(() => { >+ $.evalScript(`const shouldThrow = 42`); >+}, `SyntaxError: Can't create duplicate variable that shadows a global property: 'shouldThrow'`); >diff --git a/JSTests/stress/global-static-variables-should-not-be-shadowed-by-lexical-bindings.js b/JSTests/stress/global-static-variables-should-not-be-shadowed-by-lexical-bindings.js >new file mode 100644 >index 0000000000000000000000000000000000000000..307d3e8359d8433bbca0c4e3a65af5b0edf51d69 >--- /dev/null >+++ b/JSTests/stress/global-static-variables-should-not-be-shadowed-by-lexical-bindings.js >@@ -0,0 +1,18 @@ >+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)}`); >+} >+ >+shouldThrow(() => { >+ $.evalScript(`const NaN = 42`); >+}, `SyntaxError: Can't create duplicate variable that shadows a global property: 'NaN'`); >diff --git a/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js b/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js >new file mode 100644 >index 0000000000000000000000000000000000000000..20b4322e07d016e12fd6c03584b6dc32c240da76 >--- /dev/null >+++ b/JSTests/stress/let-lexical-binding-shadow-existing-global-property-ftl.js >@@ -0,0 +1,47 @@ >+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 shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error(`bad value: ${String(actual)}`); >+} >+noInline(shouldBe); >+ >+function foo() { >+ bar = 4; >+} >+function get() { >+ return bar; >+} >+for (var i = 0; i < 1e6; ++i) >+ foo(); >+for (var i = 0; i < 1e6; ++i) >+ shouldBe(get(), 4); >+ >+shouldBe(bar, 4); >+$.evalScript('let bar = 3;'); >+shouldBe(bar, 3); >+shouldBe(get(), 3); >+ >+for (var i = 0; i < 1e6; ++i) >+ shouldBe(get(), 3); >+ >+foo(); >+shouldBe(bar, 4); >+shouldBe(get(), 4); >+ >+for (var i = 0; i < 1e6; ++i) >+ shouldBe(get(), 4); >diff --git a/JSTests/stress/let-lexical-binding-shadow-existing-global-property-tdz-ftl.js b/JSTests/stress/let-lexical-binding-shadow-existing-global-property-tdz-ftl.js >new file mode 100644 >index 0000000000000000000000000000000000000000..ab5b61f02e2d8820d87f492356a8f349e5182345 >--- /dev/null >+++ b/JSTests/stress/let-lexical-binding-shadow-existing-global-property-tdz-ftl.js >@@ -0,0 +1,53 @@ >+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 shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error(`bad value: ${String(actual)}`); >+} >+noInline(shouldBe); >+ >+function foo() { >+ bar = 4; >+} >+function get() { >+ return bar; >+} >+for (var i = 0; i < 1e6; ++i) >+ foo(); >+for (var i = 0; i < 1e6; ++i) >+ shouldBe(get(), 4); >+ >+shouldBe(bar, 4); >+shouldThrow(() => { >+ $.evalScript('get(); let bar = 3;'); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ shouldBe(bar, 3); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ shouldBe(get(), 3); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ $.evalScript('bar;'); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+ >+for (var i = 0; i < 1e3; ++i) { >+ shouldThrow(() => { >+ shouldBe(get(), 3); >+ }, `ReferenceError: Cannot access uninitialized variable.`); >+} >+ >diff --git a/JSTests/stress/let-lexical-binding-shadow-existing-global-property-tdz.js b/JSTests/stress/let-lexical-binding-shadow-existing-global-property-tdz.js >new file mode 100644 >index 0000000000000000000000000000000000000000..3074a02c1a4758fd61ad9582884680139061caa2 >--- /dev/null >+++ b/JSTests/stress/let-lexical-binding-shadow-existing-global-property-tdz.js >@@ -0,0 +1,44 @@ >+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 shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error(`bad value: ${String(actual)}`); >+} >+noInline(shouldBe); >+ >+function foo() { >+ bar = 4; >+} >+function get() { >+ return bar; >+} >+foo(); >+shouldBe(get(), 4); >+ >+shouldBe(bar, 4); >+shouldThrow(() => { >+ $.evalScript('get(); let bar = 3;'); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ shouldBe(bar, 3); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ shouldBe(get(), 3); >+}, `ReferenceError: Cannot access uninitialized variable.`); >+shouldThrow(() => { >+ $.evalScript('bar;'); >+}, `ReferenceError: Cannot access uninitialized variable.`); >diff --git a/JSTests/stress/let-lexical-binding-shadow-existing-global-property.js b/JSTests/stress/let-lexical-binding-shadow-existing-global-property.js >new file mode 100644 >index 0000000000000000000000000000000000000000..0ccfd587f6ae2495fb65bb147a5f46d21ba9b3c9 >--- /dev/null >+++ b/JSTests/stress/let-lexical-binding-shadow-existing-global-property.js >@@ -0,0 +1,31 @@ >+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 shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error(`bad value: ${String(actual)}`); >+} >+noInline(shouldBe); >+ >+function foo() { >+ bar = 4; >+} >+foo(); >+shouldBe(bar, 4); >+$.evalScript('let bar = 3;'); >+shouldBe(bar, 3); >+foo(); >+shouldBe(bar, 4); >diff --git a/JSTests/stress/let-lexical-binding-shadowing-global-properties-and-eval-injection.js b/JSTests/stress/let-lexical-binding-shadowing-global-properties-and-eval-injection.js >new file mode 100644 >index 0000000000000000000000000000000000000000..b2bccb8f15810ce1725adfe8da5b81ca4961ea43 >--- /dev/null >+++ b/JSTests/stress/let-lexical-binding-shadowing-global-properties-and-eval-injection.js >@@ -0,0 +1,36 @@ >+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 shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error(`bad value: ${String(actual)}`); >+} >+noInline(shouldBe); >+ >+bar = 0; >+function foo(code) { >+ eval(code); >+ return (function () { >+ return bar; >+ }()); >+} >+shouldBe(foo(`42`), 0); >+ >+$.evalScript(`let bar = 42`); >+shouldBe(foo(`42`), 42); >+ >+shouldBe(foo(`var bar = 1`), 1); >+shouldBe(foo(`42`), 42);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
saam
:
review+
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 193308
:
358776
|
358777
|
358811
|
358827
|
358845
|
358855
|
358872
|
358875
|
358877
|
358879
|
358912
|
358927
| 358929 |
358942