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