ScopeBindingCache.h (11945B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef frontend_ScopeBindingCache_h 8 #define frontend_ScopeBindingCache_h 9 10 #include "mozilla/Assertions.h" // mozilla::MakeCompilerAssumeUnreachableFakeValue 11 #include "mozilla/Attributes.h" // MOZ_STACK_CLASS 12 #include "mozilla/HashTable.h" // mozilla::HashMap 13 14 #include "jstypes.h" // JS_PUBLIC_API 15 16 #include "frontend/NameAnalysisTypes.h" // NameLocation 17 #include "frontend/ParserAtom.h" // TaggedParserAtomIndex, ParserAtomsTable 18 19 #include "js/Utility.h" // AutoEnterOOMUnsafeRegion 20 21 #include "vm/StringType.h" // JSAtom 22 23 namespace js { 24 25 template <typename NameT> 26 class AbstractBaseScopeData; 27 28 namespace frontend { 29 30 struct CompilationAtomCache; 31 struct CompilationStencil; 32 struct ScopeStencilRef; 33 struct FakeStencilGlobalScope; 34 struct CompilationStencilMerger; 35 36 // Generic atom wrapper which provides a way to interpret any Atom given 37 // contextual information. Thus, this structure offers the ability to compare 38 // Atom from different domains. 39 // 40 // This structure provides a `hash` field which is universal across all Atom 41 // representations. Thus, Atoms from different contexes can be registered in a 42 // hash table and looked up with a different Atom kind. 43 struct GenericAtom { 44 // Emitter names are TaggedParserAtomIndex which are registered in the 45 // ParserAtomsTable of an extensible compilation stencil, frequently related 46 // to bytecode emitter, which lookup names in the scope chain to replace names 47 // by variable locations. 48 struct EmitterName { 49 FrontendContext* fc; 50 ParserAtomsTable& parserAtoms; 51 CompilationAtomCache& atomCache; 52 TaggedParserAtomIndex index; 53 54 EmitterName(FrontendContext* fc, ParserAtomsTable& parserAtoms, 55 CompilationAtomCache& atomCache, TaggedParserAtomIndex index) 56 : fc(fc), 57 parserAtoms(parserAtoms), 58 atomCache(atomCache), 59 index(index) {} 60 }; 61 62 // Stencil names are TaggedParserAtomIndex which are registered in a 63 // ParserAtomVector of a compilation stencil, frequently related to the result 64 // of a compilation. It can be seen while manipulating names of a scope chain 65 // while delazifying functions using a stencil for context. 66 struct StencilName { 67 const CompilationStencil& stencil; 68 TaggedParserAtomIndex index; 69 }; 70 71 // Any names are references to different Atom representation, including some 72 // which are interpretable given some contexts such as EmitterName and 73 // StencilName. 74 using AnyName = mozilla::Variant<EmitterName, StencilName, JSAtom*>; 75 76 HashNumber hash; 77 AnyName ref; 78 79 // Constructor for atoms managed by an ExtensibleCompilationState, while 80 // compiling a script. 81 GenericAtom(FrontendContext* fc, ParserAtomsTable& parserAtoms, 82 CompilationAtomCache& atomCache, TaggedParserAtomIndex index); 83 84 // Constructors for atoms managed by a CompilationStencil or a 85 // BorrowingCompilationStencil, which provide contextual information from an 86 // already compiled script. 87 GenericAtom(const CompilationStencil& context, TaggedParserAtomIndex index); 88 GenericAtom(ScopeStencilRef& scope, TaggedParserAtomIndex index); 89 GenericAtom(const FakeStencilGlobalScope& scope, TaggedParserAtomIndex index) 90 : ref((JSAtom*)nullptr) { 91 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(); 92 } 93 94 // Constructor for atoms managed by the Garbage Collector, while providing 95 // contextual scope information when delazifying functions on the main thread. 96 GenericAtom(const Scope*, JSAtom* ptr) : GenericAtom(ptr) {} 97 explicit GenericAtom(JSAtom* ptr) : ref(ptr) { hash = ptr->hash(); } 98 99 bool operator==(const GenericAtom& other) const; 100 }; 101 102 template <typename NameT> 103 struct BindingHasher; 104 105 template <> 106 struct BindingHasher<TaggedParserAtomIndex> { 107 // This is a GenericAtom::StencilName stripped from its context which is the 108 // same for every key. 109 using Key = TaggedParserAtomIndex; 110 struct Lookup { 111 // When building a BindingMap, we assume that the TaggedParserAtomIndex is 112 // coming from an existing Stencil, and is not an EmitterName. 113 const CompilationStencil& keyStencil; 114 GenericAtom other; 115 116 Lookup(ScopeStencilRef& scope_ref, const GenericAtom& other); 117 Lookup(const FakeStencilGlobalScope& scope_ref, const GenericAtom& other) 118 : keyStencil(mozilla::MakeCompilerAssumeUnreachableFakeValue< 119 const CompilationStencil&>()), 120 other(other) { 121 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(); 122 } 123 }; 124 125 static HashNumber hash(const Lookup& aLookup) { return aLookup.other.hash; } 126 127 static bool match(const Key& aKey, const Lookup& aLookup) { 128 GenericAtom key(aLookup.keyStencil, aKey); 129 return key == aLookup.other; 130 } 131 }; 132 133 template <> 134 struct BindingHasher<JSAtom*> { 135 using Key = JSAtom*; 136 struct Lookup { 137 GenericAtom other; 138 139 template <typename Any> 140 Lookup(const Any&, const GenericAtom& other) : other(other) {} 141 }; 142 143 static HashNumber hash(const Lookup& aLookup) { return aLookup.other.hash; } 144 145 static bool match(const Key& aKey, const Lookup& aLookup) { 146 GenericAtom key(aKey); 147 return key == aLookup.other; 148 } 149 }; 150 151 // Map the bound names to their respective name location. This is used to avoid 152 // doing a linear lookup over the list of bindings each time we are looking for 153 // a single name. 154 // 155 // The names given used as a key are either JSAtom in the case of a on-demand 156 // delazification, or a TaggedParserAtomIndex in case of a 157 // concurrent-delazification. In both case Lookup arguments are not trivially 158 // created out of a key, as in the case of a TaggedParserAtomIndex, the 159 // CompilationStencil should be provided to interpret the TaggedParserAtomIndex 160 // which are stored in this hash table. 161 template <typename NameT> 162 struct BindingMap { 163 using Lookup = typename BindingHasher<NameT>::Lookup; 164 using Map = 165 HashMap<NameT, NameLocation, BindingHasher<NameT>, js::SystemAllocPolicy>; 166 167 Map hashMap; 168 mozilla::Maybe<NameLocation> catchAll; 169 }; 170 171 // For each list of bound names, map the list of bound names to the hash table 172 // which is used to reduce the time needed per lookup. 173 // 174 // The name parameter are either JSAtom in the case of a on-demand 175 // delazification, or a TaggedParserAtomIndex in case of a 176 // concurrent-delazification. 177 template <typename NameT, typename ScopeT = NameT> 178 using ScopeBindingMap = 179 HashMap<AbstractBaseScopeData<ScopeT>*, BindingMap<NameT>, 180 DefaultHasher<AbstractBaseScopeData<ScopeT>*>, 181 js::SystemAllocPolicy>; 182 183 // Common interface for a cache holding the mapping of Scope to a hash table 184 // which mirror the binding mapping stored in the scope. 185 class ScopeBindingCache { 186 public: 187 using CacheGeneration = size_t; 188 189 virtual CacheGeneration getCurrentGeneration() const = 0; 190 191 // Check whether the cache provided as argument is capable of storing the type 192 // of scope given as arguments. 193 virtual bool canCacheFor(Scope* ptr); 194 virtual bool canCacheFor(ScopeStencilRef ref); 195 virtual bool canCacheFor(const FakeStencilGlobalScope& ref); 196 197 // Create a new BindingMap cache for a given scope. This cache should then be 198 // filled with all names which might be looked up. 199 virtual BindingMap<JSAtom*>* createCacheFor(Scope* ptr); 200 virtual BindingMap<TaggedParserAtomIndex>* createCacheFor( 201 ScopeStencilRef ref); 202 virtual BindingMap<TaggedParserAtomIndex>* createCacheFor( 203 const FakeStencilGlobalScope& ref); 204 205 // Return the BindingMap created for the associated scope, unless the 206 // generation value does not match the one stored internally, in which case a 207 // null pointer is always returned. 208 virtual BindingMap<JSAtom*>* lookupScope(Scope* ptr, CacheGeneration gen); 209 virtual BindingMap<TaggedParserAtomIndex>* lookupScope(ScopeStencilRef ref, 210 CacheGeneration gen); 211 virtual BindingMap<TaggedParserAtomIndex>* lookupScope( 212 const FakeStencilGlobalScope& ref, CacheGeneration gen); 213 }; 214 215 // NoScopeBindingCache is a no-op which does not implement a ScopeBindingCache. 216 // 217 // This is useful when compiling a global script or module, where we are not 218 // interested in looking up anything from the enclosing scope chain. 219 class NoScopeBindingCache final : public ScopeBindingCache { 220 public: 221 CacheGeneration getCurrentGeneration() const override { return 1; }; 222 223 bool canCacheFor(Scope* ptr) override; 224 bool canCacheFor(ScopeStencilRef ref) override; 225 bool canCacheFor(const FakeStencilGlobalScope& ref) override; 226 }; 227 228 // StencilScopeBindingCache provides an interface to cache the bindings provided 229 // by a CompilationStencilMerger. 230 // 231 // This cache lives on the stack and its content would be invalidated once going 232 // out of scope. The constructor expects a reference to a 233 // CompilationStencilMerger, that is expected to: 234 // - out-live this class. 235 // - contain the enclosing scope which are manipulated by this class. 236 // - be the receiver of delazified functions. 237 class MOZ_STACK_CLASS StencilScopeBindingCache final 238 : public ScopeBindingCache { 239 ScopeBindingMap<TaggedParserAtomIndex> scopeMap; 240 #ifdef DEBUG 241 const InitialStencilAndDelazifications& stencils_; 242 #endif 243 244 public: 245 explicit StencilScopeBindingCache( 246 const InitialStencilAndDelazifications& stencils) 247 #ifdef DEBUG 248 : stencils_(stencils) 249 #endif 250 { 251 } 252 253 // The cache content is always valid as long as it does not out-live the 254 // CompilationStencilMerger. No need for a generation number. 255 CacheGeneration getCurrentGeneration() const override { return 1; } 256 257 bool canCacheFor(ScopeStencilRef ref) override; 258 bool canCacheFor(const FakeStencilGlobalScope& ref) override; 259 260 BindingMap<TaggedParserAtomIndex>* createCacheFor( 261 ScopeStencilRef ref) override; 262 BindingMap<TaggedParserAtomIndex>* createCacheFor( 263 const FakeStencilGlobalScope& ref) override; 264 265 BindingMap<TaggedParserAtomIndex>* lookupScope(ScopeStencilRef ref, 266 CacheGeneration gen) override; 267 BindingMap<TaggedParserAtomIndex>* lookupScope( 268 const FakeStencilGlobalScope& ref, CacheGeneration gen) override; 269 }; 270 271 // RuntimeScopeBindingCache is used to hold the binding map for each scope which 272 // is hold by a Scope managed by the garbage collector. 273 // 274 // This cache is not thread safe. 275 // 276 // The generation number is used to assert the validity of the cached content. 277 // During a GC, the cached content is thrown away and getCurrentGeneration 278 // returns a different number. When the generation number differs from the 279 // initialization of the cached content, the cache content might be renewed or 280 // ignored. 281 class RuntimeScopeBindingCache final : public ScopeBindingCache { 282 ScopeBindingMap<JSAtom*, JSAtom> scopeMap; 283 284 // This value is initialized to 1, such that we can differentiate it from the 285 // typical 0-init of size_t values, when non-initialized. 286 size_t cacheGeneration = 1; 287 288 public: 289 CacheGeneration getCurrentGeneration() const override { 290 return cacheGeneration; 291 } 292 293 bool canCacheFor(Scope* ptr) override; 294 BindingMap<JSAtom*>* createCacheFor(Scope* ptr) override; 295 BindingMap<JSAtom*>* lookupScope(Scope* ptr, CacheGeneration gen) override; 296 297 // The ScopeBindingCache is not instrumented for tracing weakly the keys used 298 // for mapping to the NameLocation. Instead, we always purge during compaction 299 // or collection, and increment the cacheGeneration to notify all consumers 300 // that the cache can no longer be used without being re-populated. 301 void purge() { 302 cacheGeneration++; 303 scopeMap.clearAndCompact(); 304 } 305 }; 306 307 } // namespace frontend 308 } // namespace js 309 310 #endif // frontend_ScopeBindingCache_h