Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / frontend / CompilationStencil.h
blobc1e98f47540734c1c735c70dfec235f265db450b
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_CompilationStencil_h
8 #define frontend_CompilationStencil_h
10 #include "mozilla/AlreadyAddRefed.h" // already_AddRefed
11 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_RELEASE_ASSERT, MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE
12 #include "mozilla/Atomics.h" // mozilla::Atomic
13 #include "mozilla/Attributes.h" // MOZ_RAII, MOZ_STACK_CLASS
14 #include "mozilla/HashTable.h" // mozilla::HashMap, mozilla::DefaultHasher
15 #include "mozilla/Maybe.h" // mozilla::Maybe
16 #include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf
17 #include "mozilla/RefPtr.h" // RefPtr
18 #include "mozilla/Span.h" // mozilla::Span
19 #include "mozilla/Variant.h" // mozilla::Variant
21 #include <algorithm> // std::swap
22 #include <stddef.h> // size_t
23 #include <stdint.h> // uint32_t, uintptr_t
24 #include <type_traits> // std::is_pointer_v
25 #include <utility> // std::forward, std::move
27 #include "ds/LifoAlloc.h" // LifoAlloc, LifoAllocScope
28 #include "frontend/FrontendContext.h" // FrontendContext
29 #include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind
30 #include "frontend/NameAnalysisTypes.h" // NameLocation
31 #include "frontend/ParserAtom.h" // ParserAtomsTable, ParserAtomIndex, TaggedParserAtomIndex, ParserAtomSpan
32 #include "frontend/ScopeIndex.h" // ScopeIndex
33 #include "frontend/ScriptIndex.h" // ScriptIndex
34 #include "frontend/SharedContext.h" // ThisBinding, InheritThis, Directives
35 #include "frontend/Stencil.h" // ScriptStencil, ScriptStencilExtra, ScopeStencil, RegExpStencil, BigIntStencil, ObjLiteralStencil, BaseParserScopeData, StencilModuleMetadata
36 #include "frontend/TaggedParserAtomIndexHasher.h" // TaggedParserAtomIndexHasher
37 #include "frontend/UsedNameTracker.h" // UsedNameTracker
38 #include "js/AllocPolicy.h" // SystemAllocPolicy, ReportOutOfMemory
39 #include "js/GCVector.h" // JS::GCVector
40 #include "js/RefCounted.h" // AtomicRefCounted
41 #include "js/RootingAPI.h" // JS::Handle
42 #include "js/Transcoding.h" // JS::TranscodeBuffer, JS::TranscodeRange
43 #include "js/UniquePtr.h" // js::UniquePtr
44 #include "js/Vector.h" // Vector
45 #include "js/WasmModule.h" // JS::WasmModule
46 #include "vm/FunctionFlags.h" // FunctionFlags
47 #include "vm/GlobalObject.h" // GlobalObject
48 #include "vm/JSContext.h" // JSContext
49 #include "vm/JSFunction.h" // JSFunction
50 #include "vm/JSScript.h" // BaseScript, ScriptSource, SourceExtent
51 #include "vm/Realm.h" // JSContext::global
52 #include "vm/Scope.h" // Scope, ModuleScope
53 #include "vm/ScopeKind.h" // ScopeKind
54 #include "vm/SharedStencil.h" // ImmutableScriptFlags, MemberInitializers, SharedImmutableScriptData, RO_IMMUTABLE_SCRIPT_FLAGS
56 class JSAtom;
57 class JSFunction;
58 class JSObject;
59 class JSString;
60 class JSTracer;
62 namespace JS {
63 class JS_PUBLIC_API ReadOnlyCompileOptions;
66 namespace js {
68 class AtomSet;
69 class JSONPrinter;
70 class ModuleObject;
72 namespace frontend {
74 struct CompilationInput;
75 struct CompilationStencil;
76 struct CompilationGCOutput;
77 struct PreallocatedCompilationGCOutput;
78 class ScriptStencilIterable;
79 struct InputName;
80 class ScopeBindingCache;
82 // When delazifying modules' inner functions, the actual global scope is used.
83 // However, when doing a delazification the global scope is not available. We
84 // use this dummy type to be a placeholder to be used as part of the InputScope
85 // variants to mimic what the Global scope would be used for.
86 struct FakeStencilGlobalScope {};
88 // Reference to a Scope within a CompilationStencil.
89 struct ScopeStencilRef {
90 const CompilationStencil& context_;
91 const ScopeIndex scopeIndex_;
93 // Lookup the ScopeStencil referenced by this ScopeStencilRef.
94 inline const ScopeStencil& scope() const;
97 // Wraps a scope for a CompilationInput. The scope is either as a GC pointer to
98 // an instantiated scope, or as a reference to a CompilationStencil.
100 // Note: A scope reference may be nullptr/InvalidIndex if there is no such
101 // scope, such as the enclosingScope at the end of a scope chain. See `isNull`
102 // helper.
103 class InputScope {
104 using InputScopeStorage =
105 mozilla::Variant<Scope*, ScopeStencilRef, FakeStencilGlobalScope>;
106 InputScopeStorage scope_;
108 public:
109 // Create an InputScope given an instantiated scope.
110 explicit InputScope(Scope* ptr) : scope_(ptr) {}
112 // Create an InputScope for a global.
113 explicit InputScope(FakeStencilGlobalScope global) : scope_(global) {}
115 // Create an InputScope given a CompilationStencil and the ScopeIndex which is
116 // an offset within the same CompilationStencil given as argument.
117 InputScope(const CompilationStencil& context, ScopeIndex scopeIndex)
118 : scope_(ScopeStencilRef{context, scopeIndex}) {}
120 // Returns the variant used by the InputScope. This can be useful for complex
121 // cases where the following accessors are not enough.
122 const InputScopeStorage& variant() const { return scope_; }
123 InputScopeStorage& variant() { return scope_; }
125 // This match function will unwrap the variant for each type, and will
126 // specialize and call the Matcher given as argument with the type and value
127 // of the stored pointer / reference.
129 // This is useful for cases where the code is literally identical despite
130 // having different specializations. This is achiveable by relying on
131 // function overloading when the usage differ between the 2 types.
133 // Example:
134 // inputScope.match([](auto& scope) {
135 // // scope is either a `Scope*` or a `ScopeStencilRef`.
136 // for (auto bi = InputBindingIter(scope); bi; bi++) {
137 // InputName name(scope, bi.name());
138 // // ...
139 // }
140 // });
141 template <typename Matcher>
142 decltype(auto) match(Matcher&& matcher) const& {
143 return scope_.match(std::forward<Matcher>(matcher));
145 template <typename Matcher>
146 decltype(auto) match(Matcher&& matcher) & {
147 return scope_.match(std::forward<Matcher>(matcher));
150 bool isNull() const {
151 return scope_.match(
152 [](const Scope* ptr) { return !ptr; },
153 [](const ScopeStencilRef& ref) { return !ref.scopeIndex_.isValid(); },
154 [](const FakeStencilGlobalScope&) { return false; });
157 ScopeKind kind() const {
158 return scope_.match(
159 [](const Scope* ptr) { return ptr->kind(); },
160 [](const ScopeStencilRef& ref) { return ref.scope().kind(); },
161 [](const FakeStencilGlobalScope&) { return ScopeKind::Global; });
163 bool hasEnvironment() const {
164 return scope_.match(
165 [](const Scope* ptr) { return ptr->hasEnvironment(); },
166 [](const ScopeStencilRef& ref) { return ref.scope().hasEnvironment(); },
167 [](const FakeStencilGlobalScope&) {
168 // See Scope::hasEnvironment
169 return true;
172 inline InputScope enclosing() const;
173 bool hasOnChain(ScopeKind kind) const {
174 return scope_.match(
175 [=](const Scope* ptr) { return ptr->hasOnChain(kind); },
176 [=](const ScopeStencilRef& ref) {
177 ScopeStencilRef it = ref;
178 while (true) {
179 const ScopeStencil& scope = it.scope();
180 if (scope.kind() == kind) {
181 return true;
183 if (scope.kind() == ScopeKind::Module &&
184 kind == ScopeKind::Global) {
185 return true;
187 if (!scope.hasEnclosing()) {
188 break;
190 new (&it) ScopeStencilRef{ref.context_, scope.enclosing()};
192 return false;
194 [=](const FakeStencilGlobalScope&) {
195 return kind == ScopeKind::Global;
198 uint32_t environmentChainLength() const {
199 return scope_.match(
200 [](const Scope* ptr) { return ptr->environmentChainLength(); },
201 [](const ScopeStencilRef& ref) {
202 uint32_t length = 0;
203 ScopeStencilRef it = ref;
204 while (true) {
205 const ScopeStencil& scope = it.scope();
206 if (scope.hasEnvironment() &&
207 scope.kind() != ScopeKind::NonSyntactic) {
208 length++;
210 if (scope.kind() == ScopeKind::Module) {
211 // Stencil do not encode the Global scope, as it used to be
212 // assumed to already exists. As moving delazification off-thread,
213 // we need to materialize a fake-stencil version of the Global
214 // Scope.
215 MOZ_ASSERT(!scope.hasEnclosing());
216 length += js::ModuleScope::EnclosingEnvironmentChainLength;
218 if (!scope.hasEnclosing()) {
219 break;
221 new (&it) ScopeStencilRef{ref.context_, scope.enclosing()};
223 return length;
225 [=](const FakeStencilGlobalScope&) {
226 // Stencil-based delazification needs to calculate
227 // environmentChainLength where the global is not available.
229 // The FakeStencilGlobalScope is used to represent what the global
230 // would be if we had access to it while delazifying.
231 return uint32_t(js::ModuleScope::EnclosingEnvironmentChainLength);
234 void trace(JSTracer* trc);
235 bool isStencil() const { return !scope_.is<Scope*>(); };
237 // Various accessors which are valid only when the InputScope is a
238 // FunctionScope. Some of these accessors are returning values associated with
239 // the canonical function.
240 private:
241 inline FunctionFlags functionFlags() const;
242 inline ImmutableScriptFlags immutableFlags() const;
244 public:
245 inline MemberInitializers getMemberInitializers() const;
246 RO_IMMUTABLE_SCRIPT_FLAGS(immutableFlags())
247 bool isArrow() const { return functionFlags().isArrow(); }
248 bool allowSuperProperty() const {
249 return functionFlags().allowSuperProperty();
251 bool isClassConstructor() const {
252 return functionFlags().isClassConstructor();
256 // Reference to a Script within a CompilationStencil.
257 struct ScriptStencilRef {
258 const CompilationStencil& context_;
259 const ScriptIndex scriptIndex_;
261 inline const ScriptStencil& scriptData() const;
262 inline const ScriptStencilExtra& scriptExtra() const;
265 // Wraps a script for a CompilationInput. The script is either as a BaseScript
266 // pointer to an instantiated script, or as a reference to a CompilationStencil.
267 class InputScript {
268 using InputScriptStorage = mozilla::Variant<BaseScript*, ScriptStencilRef>;
269 InputScriptStorage script_;
271 public:
272 // Create an InputScript given an instantiated BaseScript pointer.
273 explicit InputScript(BaseScript* ptr) : script_(ptr) {}
275 // Create an InputScript given a CompilationStencil and the ScriptIndex which
276 // is an offset within the same CompilationStencil given as argument.
277 InputScript(const CompilationStencil& context, ScriptIndex scriptIndex)
278 : script_(ScriptStencilRef{context, scriptIndex}) {}
280 const InputScriptStorage& raw() const { return script_; }
281 InputScriptStorage& raw() { return script_; }
283 SourceExtent extent() const {
284 return script_.match(
285 [](const BaseScript* ptr) { return ptr->extent(); },
286 [](const ScriptStencilRef& ref) { return ref.scriptExtra().extent; });
288 ImmutableScriptFlags immutableFlags() const {
289 return script_.match(
290 [](const BaseScript* ptr) { return ptr->immutableFlags(); },
291 [](const ScriptStencilRef& ref) {
292 return ref.scriptExtra().immutableFlags;
295 RO_IMMUTABLE_SCRIPT_FLAGS(immutableFlags())
296 FunctionFlags functionFlags() const {
297 return script_.match(
298 [](const BaseScript* ptr) { return ptr->function()->flags(); },
299 [](const ScriptStencilRef& ref) {
300 return ref.scriptData().functionFlags;
303 bool hasPrivateScriptData() const {
304 return script_.match(
305 [](const BaseScript* ptr) { return ptr->hasPrivateScriptData(); },
306 [](const ScriptStencilRef& ref) {
307 // See BaseScript::CreateRawLazy.
308 return ref.scriptData().hasGCThings() ||
309 ref.scriptExtra().useMemberInitializers();
312 InputScope enclosingScope() const {
313 return script_.match(
314 [](const BaseScript* ptr) {
315 return InputScope(ptr->function()->enclosingScope());
317 [](const ScriptStencilRef& ref) {
318 // The ScriptStencilRef only reference lazy Script, otherwise we
319 // should fetch the enclosing scope using the bodyScope field of the
320 // immutable data which is a reference to the vector of gc-things.
321 MOZ_RELEASE_ASSERT(!ref.scriptData().hasSharedData());
322 MOZ_ASSERT(ref.scriptData().hasLazyFunctionEnclosingScopeIndex());
323 auto scopeIndex = ref.scriptData().lazyFunctionEnclosingScopeIndex();
324 return InputScope(ref.context_, scopeIndex);
327 MemberInitializers getMemberInitializers() const {
328 return script_.match(
329 [](const BaseScript* ptr) { return ptr->getMemberInitializers(); },
330 [](const ScriptStencilRef& ref) {
331 return ref.scriptExtra().memberInitializers();
335 InputName displayAtom() const;
336 void trace(JSTracer* trc);
337 bool isNull() const {
338 return script_.match([](const BaseScript* ptr) { return !ptr; },
339 [](const ScriptStencilRef& ref) { return false; });
341 bool isStencil() const {
342 return script_.match([](const BaseScript* ptr) { return false; },
343 [](const ScriptStencilRef&) { return true; });
347 // Iterator for walking the scope chain, this is identical to ScopeIter but
348 // accept an InputScope instead of a Scope pointer.
350 // It may be placed in GC containers; for example:
352 // for (Rooted<InputScopeIter> si(cx, InputScopeIter(scope)); si; si++) {
353 // use(si);
354 // SomeMayGCOperation();
355 // use(si);
356 // }
358 class MOZ_STACK_CLASS InputScopeIter {
359 InputScope scope_;
361 public:
362 explicit InputScopeIter(const InputScope& scope) : scope_(scope) {}
364 InputScope& scope() {
365 MOZ_ASSERT(!done());
366 return scope_;
369 const InputScope& scope() const {
370 MOZ_ASSERT(!done());
371 return scope_;
374 bool done() const { return scope_.isNull(); }
375 explicit operator bool() const { return !done(); }
376 void operator++(int) { scope_ = scope_.enclosing(); }
377 ScopeKind kind() const { return scope_.kind(); }
379 // Returns whether this scope has a syntactic environment (i.e., an
380 // Environment that isn't a non-syntactic With or NonSyntacticVariables)
381 // on the environment chain.
382 bool hasSyntacticEnvironment() const {
383 return scope_.hasEnvironment() && scope_.kind() != ScopeKind::NonSyntactic;
386 void trace(JSTracer* trc) { scope_.trace(trc); }
389 // Reference to a Binding Name within an existing CompilationStencil.
390 // TaggedParserAtomIndex are in some cases indexes in the parserAtomData of the
391 // CompilationStencil.
392 struct NameStencilRef {
393 const CompilationStencil& context_;
394 const TaggedParserAtomIndex atomIndex_;
397 // Wraps a name for a CompilationInput. The name is either as a GC pointer to
398 // a JSAtom, or a TaggedParserAtomIndex which might reference to a non-included.
400 // The constructor for this class are using an InputScope as argument. This
401 // InputScope is made to fetch back the CompilationStencil associated with the
402 // TaggedParserAtomIndex when using a Stencil as input.
403 struct InputName {
404 using InputNameStorage = mozilla::Variant<JSAtom*, NameStencilRef>;
405 InputNameStorage variant_;
407 InputName(Scope*, JSAtom* ptr) : variant_(ptr) {}
408 InputName(const ScopeStencilRef& scope, TaggedParserAtomIndex index)
409 : variant_(NameStencilRef{scope.context_, index}) {}
410 InputName(BaseScript*, JSAtom* ptr) : variant_(ptr) {}
411 InputName(const ScriptStencilRef& script, TaggedParserAtomIndex index)
412 : variant_(NameStencilRef{script.context_, index}) {}
414 // Dummy for empty global.
415 InputName(const FakeStencilGlobalScope&, TaggedParserAtomIndex)
416 : variant_(static_cast<JSAtom*>(nullptr)) {}
418 // The InputName is either from an instantiated name, or from another
419 // CompilationStencil. This method interns the current name in the parser atom
420 // table of a CompilationState, which has a corresponding CompilationInput.
421 TaggedParserAtomIndex internInto(FrontendContext* fc,
422 ParserAtomsTable& parserAtoms,
423 CompilationAtomCache& atomCache);
425 // Compare an InputName, which is not yet interned, with `other` is either an
426 // interned name or a well-known or static string.
428 // The `otherCached` argument should be a reference to a JSAtom*, initialized
429 // to nullptr, which is used to cache the JSAtom representation of the `other`
430 // argument if needed. If a different `other` parameter is provided, the
431 // `otherCached` argument should be reset to nullptr.
432 bool isEqualTo(FrontendContext* fc, ParserAtomsTable& parserAtoms,
433 CompilationAtomCache& atomCache, TaggedParserAtomIndex other,
434 JSAtom** otherCached) const;
436 bool isNull() const {
437 return variant_.match(
438 [](JSAtom* ptr) { return !ptr; },
439 [](const NameStencilRef& ref) { return !ref.atomIndex_; });
443 // ScopeContext holds information derived from the scope and environment chains
444 // to try to avoid the parser needing to traverse VM structures directly.
445 struct ScopeContext {
446 // Cache: Scope -> (JSAtom/TaggedParserAtomIndex -> NameLocation)
448 // This cache maps the scope to a hash table which can lookup a name of the
449 // scope to the equivalent NameLocation.
450 ScopeBindingCache* scopeCache = nullptr;
452 // Generation number of the `scopeCache` collected before filling the cache
453 // with enclosing scope information.
455 // The generation number, obtained from `scopeCache->getCurrentGeneration()`
456 // is incremented each time the GC invalidate the content of the cache. The
457 // `scopeCache` can only be used when the generation number collected before
458 // filling the cache is identical to the generation number seen when querying
459 // the cached content.
460 size_t scopeCacheGen = 0;
462 // Class field initializer info if we are nested within a class constructor.
463 // We may be an combination of arrow and eval context within the constructor.
464 mozilla::Maybe<MemberInitializers> memberInitializers = {};
466 enum class EnclosingLexicalBindingKind {
467 Let,
468 Const,
469 CatchParameter,
470 Synthetic,
471 PrivateMethod,
474 using EnclosingLexicalBindingCache =
475 mozilla::HashMap<TaggedParserAtomIndex, EnclosingLexicalBindingKind,
476 TaggedParserAtomIndexHasher>;
478 // Cache of enclosing lexical bindings.
479 // Used only for eval.
480 mozilla::Maybe<EnclosingLexicalBindingCache> enclosingLexicalBindingCache_;
482 // A map of private names to NameLocations used to allow evals to
483 // provide correct private name semantics (particularly around early
484 // errors and private brand lookup).
485 using EffectiveScopePrivateFieldCache =
486 mozilla::HashMap<TaggedParserAtomIndex, NameLocation,
487 TaggedParserAtomIndexHasher>;
489 // Cache of enclosing class's private fields.
490 // Used only for eval.
491 mozilla::Maybe<EffectiveScopePrivateFieldCache>
492 effectiveScopePrivateFieldCache_;
494 #ifdef DEBUG
495 bool enclosingEnvironmentIsDebugProxy_ = false;
496 #endif
498 // How many hops required to navigate from 'enclosingScope' to effective
499 // scope.
500 uint32_t effectiveScopeHops = 0;
502 uint32_t enclosingScopeEnvironmentChainLength = 0;
504 // Eval and arrow scripts also inherit the "this" environment -- used by
505 // `super` expressions -- from their enclosing script. We count the number of
506 // environment hops needed to get from enclosing scope to the nearest
507 // appropriate environment. This value is undefined if the script we are
508 // compiling is not an eval or arrow-function.
509 uint32_t enclosingThisEnvironmentHops = 0;
511 // The kind of enclosing scope.
512 ScopeKind enclosingScopeKind = ScopeKind::Global;
514 // The type of binding required for `this` of the top level context, as
515 // indicated by the enclosing scopes of this parse.
517 // NOTE: This is computed based on the effective scope (defined above).
518 ThisBinding thisBinding = ThisBinding::Global;
520 // Eval and arrow scripts inherit certain syntax allowances from their
521 // enclosing scripts.
522 bool allowNewTarget = false;
523 bool allowSuperProperty = false;
524 bool allowSuperCall = false;
525 bool allowArguments = true;
527 // Indicates there is a 'class' or 'with' scope on enclosing scope chain.
528 bool inClass = false;
529 bool inWith = false;
531 // True if the enclosing scope is for FunctionScope of arrow function.
532 bool enclosingScopeIsArrow = false;
534 // True if the enclosing scope has environment.
535 bool enclosingScopeHasEnvironment = false;
537 #ifdef DEBUG
538 // True if the enclosing scope has non-syntactic scope on chain.
539 bool hasNonSyntacticScopeOnChain = false;
541 // True if the enclosing scope has function scope where the function needs
542 // home object.
543 bool hasFunctionNeedsHomeObjectOnChain = false;
544 #endif
546 bool init(FrontendContext* fc, CompilationInput& input,
547 ParserAtomsTable& parserAtoms, ScopeBindingCache* scopeCache,
548 InheritThis inheritThis, JSObject* enclosingEnv);
550 mozilla::Maybe<EnclosingLexicalBindingKind>
551 lookupLexicalBindingInEnclosingScope(TaggedParserAtomIndex name);
553 NameLocation searchInEnclosingScope(FrontendContext* fc,
554 CompilationInput& input,
555 ParserAtomsTable& parserAtoms,
556 TaggedParserAtomIndex name);
558 bool effectiveScopePrivateFieldCacheHas(TaggedParserAtomIndex name);
559 mozilla::Maybe<NameLocation> getPrivateFieldLocation(
560 TaggedParserAtomIndex name);
562 private:
563 void computeThisBinding(const InputScope& scope);
564 void computeThisEnvironment(const InputScope& enclosingScope);
565 void computeInScope(const InputScope& enclosingScope);
566 void cacheEnclosingScope(const InputScope& enclosingScope);
567 NameLocation searchInEnclosingScopeWithCache(FrontendContext* fc,
568 CompilationInput& input,
569 ParserAtomsTable& parserAtoms,
570 TaggedParserAtomIndex name);
571 NameLocation searchInEnclosingScopeNoCache(FrontendContext* fc,
572 CompilationInput& input,
573 ParserAtomsTable& parserAtoms,
574 TaggedParserAtomIndex name);
576 InputScope determineEffectiveScope(InputScope& scope, JSObject* environment);
578 bool cachePrivateFieldsForEval(FrontendContext* fc, CompilationInput& input,
579 JSObject* enclosingEnvironment,
580 const InputScope& effectiveScope,
581 ParserAtomsTable& parserAtoms);
583 bool cacheEnclosingScopeBindingForEval(FrontendContext* fc,
584 CompilationInput& input,
585 ParserAtomsTable& parserAtoms);
587 bool addToEnclosingLexicalBindingCache(FrontendContext* fc,
588 ParserAtomsTable& parserAtoms,
589 CompilationAtomCache& atomCache,
590 InputName& name,
591 EnclosingLexicalBindingKind kind);
594 struct CompilationAtomCache {
595 public:
596 using AtomCacheVector = JS::GCVector<JSString*, 0, js::SystemAllocPolicy>;
598 private:
599 // Atoms lowered into or converted from CompilationStencil.parserAtomData.
601 // This field is here instead of in CompilationGCOutput because atoms lowered
602 // from JSAtom is part of input (enclosing scope bindings, lazy function name,
603 // etc), and having 2 vectors in both input/output is error prone.
604 AtomCacheVector atoms_;
606 public:
607 JSString* getExistingStringAt(ParserAtomIndex index) const;
608 JSString* getExistingStringAt(JSContext* cx,
609 TaggedParserAtomIndex taggedIndex) const;
610 JSString* getStringAt(ParserAtomIndex index) const;
612 JSAtom* getExistingAtomAt(ParserAtomIndex index) const;
613 JSAtom* getExistingAtomAt(JSContext* cx,
614 TaggedParserAtomIndex taggedIndex) const;
615 JSAtom* getAtomAt(ParserAtomIndex index) const;
617 bool hasAtomAt(ParserAtomIndex index) const;
618 bool setAtomAt(FrontendContext* fc, ParserAtomIndex index, JSString* atom);
619 bool allocate(FrontendContext* fc, size_t length);
621 bool empty() const { return atoms_.empty(); }
622 size_t size() const { return atoms_.length(); }
624 void stealBuffer(AtomCacheVector& atoms);
625 void releaseBuffer(AtomCacheVector& atoms);
627 void trace(JSTracer* trc);
629 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
630 return atoms_.sizeOfExcludingThis(mallocSizeOf);
634 // Information associated with an extra binding provided to a global script.
635 // See frontend::CompileGlobalScriptWithExtraBindings.
636 struct ExtraBindingInfo {
637 // UTF-8 encoded name of the binding.
638 UniqueChars nameChars;
640 TaggedParserAtomIndex nameIndex;
642 // If the binding conflicts with global variable or global lexical variable,
643 // the binding is shadowed.
644 bool isShadowed = false;
646 ExtraBindingInfo(UniqueChars&& nameChars, bool isShadowed)
647 : nameChars(std::move(nameChars)), isShadowed(isShadowed) {}
650 using ExtraBindingInfoVector =
651 js::Vector<ExtraBindingInfo, 0, js::SystemAllocPolicy>;
653 // Input of the compilation, including source and enclosing context.
654 struct CompilationInput {
655 enum class CompilationTarget {
656 Global,
657 SelfHosting,
658 StandaloneFunction,
659 StandaloneFunctionInNonSyntacticScope,
660 Eval,
661 Module,
662 Delazification,
664 CompilationTarget target = CompilationTarget::Global;
666 const JS::ReadOnlyCompileOptions& options;
668 CompilationAtomCache atomCache;
670 private:
671 InputScript lazy_ = InputScript(nullptr);
673 // Extra bindings for the global script.
674 ExtraBindingInfoVector* maybeExtraBindings_ = nullptr;
676 public:
677 RefPtr<ScriptSource> source;
679 // * If the target is Global, null.
680 // * If the target is SelfHosting, null. Instantiation code for self-hosting
681 // will ignore this and use the appropriate empty global scope instead.
682 // * If the target is StandaloneFunction, an empty global scope.
683 // * If the target is StandaloneFunctionInNonSyntacticScope, the non-null
684 // enclosing scope of the function
685 // * If the target is Eval, the non-null enclosing scope of the `eval`.
686 // * If the target is Module, null that means empty global scope
687 // (See EmitterScope::checkEnvironmentChainLength)
688 // * If the target is Delazification, the non-null enclosing scope of
689 // the function
690 InputScope enclosingScope = InputScope(nullptr);
692 explicit CompilationInput(const JS::ReadOnlyCompileOptions& options)
693 : options(options) {}
695 private:
696 bool initScriptSource(FrontendContext* fc);
698 public:
699 bool initForGlobal(FrontendContext* fc) {
700 target = CompilationTarget::Global;
701 return initScriptSource(fc);
704 bool initForGlobalWithExtraBindings(
705 FrontendContext* fc, ExtraBindingInfoVector* maybeExtraBindings) {
706 MOZ_ASSERT(maybeExtraBindings);
707 target = CompilationTarget::Global;
708 maybeExtraBindings_ = maybeExtraBindings;
709 return initScriptSource(fc);
712 bool initForSelfHostingGlobal(FrontendContext* fc) {
713 target = CompilationTarget::SelfHosting;
714 return initScriptSource(fc);
717 bool initForStandaloneFunction(JSContext* cx, FrontendContext* fc) {
718 target = CompilationTarget::StandaloneFunction;
719 if (!initScriptSource(fc)) {
720 return false;
722 enclosingScope = InputScope(&cx->global()->emptyGlobalScope());
723 return true;
726 bool initForStandaloneFunctionInNonSyntacticScope(
727 FrontendContext* fc, JS::Handle<Scope*> functionEnclosingScope);
729 bool initForEval(FrontendContext* fc, JS::Handle<Scope*> evalEnclosingScope) {
730 target = CompilationTarget::Eval;
731 if (!initScriptSource(fc)) {
732 return false;
734 enclosingScope = InputScope(evalEnclosingScope);
735 return true;
738 bool initForModule(FrontendContext* fc) {
739 target = CompilationTarget::Module;
740 if (!initScriptSource(fc)) {
741 return false;
743 // The `enclosingScope` is the emptyGlobalScope.
744 return true;
747 void initFromLazy(JSContext* cx, BaseScript* lazyScript, ScriptSource* ss) {
748 MOZ_ASSERT(cx->compartment() == lazyScript->compartment());
750 // We can only compile functions whose parents have previously been
751 // compiled, because compilation requires full information about the
752 // function's immediately enclosing scope.
753 MOZ_ASSERT(lazyScript->isReadyForDelazification());
754 target = CompilationTarget::Delazification;
755 lazy_ = InputScript(lazyScript);
756 source = ss;
757 enclosingScope = lazy_.enclosingScope();
760 void initFromStencil(CompilationStencil& context, ScriptIndex scriptIndex,
761 ScriptSource* ss) {
762 target = CompilationTarget::Delazification;
763 lazy_ = InputScript(context, scriptIndex);
764 source = ss;
765 enclosingScope = lazy_.enclosingScope();
768 // Returns true if enclosingScope field is provided to init* function,
769 // instead of setting to empty global internally.
770 bool hasNonDefaultEnclosingScope() const {
771 return target == CompilationTarget::StandaloneFunctionInNonSyntacticScope ||
772 target == CompilationTarget::Eval ||
773 target == CompilationTarget::Delazification;
776 // Returns the enclosing scope provided to init* function.
777 // nullptr otherwise.
778 InputScope maybeNonDefaultEnclosingScope() const {
779 if (hasNonDefaultEnclosingScope()) {
780 return enclosingScope;
782 return InputScope(nullptr);
785 // The BaseScript* is needed when instantiating a lazy function.
786 // See InstantiateTopLevel and FunctionsFromExistingLazy.
787 InputScript lazyOuterScript() { return lazy_; }
788 BaseScript* lazyOuterBaseScript() { return lazy_.raw().as<BaseScript*>(); }
790 // The JSFunction* is needed when instantiating a lazy function.
791 // See FunctionsFromExistingLazy.
792 JSFunction* function() const {
793 return lazy_.raw().as<BaseScript*>()->function();
796 // When compiling an inner function, we want to know the unique identifier
797 // which identify a function. This is computed from the source extend.
798 SourceExtent extent() const { return lazy_.extent(); }
800 // See `BaseScript::immutableFlags_`.
801 ImmutableScriptFlags immutableFlags() const { return lazy_.immutableFlags(); }
803 RO_IMMUTABLE_SCRIPT_FLAGS(immutableFlags())
805 FunctionFlags functionFlags() const { return lazy_.functionFlags(); }
807 // When delazifying, return the kind of function which is defined.
808 FunctionSyntaxKind functionSyntaxKind() const;
810 bool hasPrivateScriptData() const {
811 // This is equivalent to: ngcthings != 0 || useMemberInitializers()
812 // See BaseScript::CreateRawLazy.
813 return lazy_.hasPrivateScriptData();
816 // Whether this CompilationInput is parsing the top-level of a script, or
817 // false if we are parsing an inner function.
818 bool isInitialStencil() { return lazy_.isNull(); }
820 // Whether this CompilationInput is parsing a specific function with already
821 // pre-parsed contextual information.
822 bool isDelazifying() { return target == CompilationTarget::Delazification; }
824 bool hasExtraBindings() const { return !!maybeExtraBindings_; }
825 ExtraBindingInfoVector& extraBindings() { return *maybeExtraBindings_; }
826 const ExtraBindingInfoVector& extraBindings() const {
827 return *maybeExtraBindings_;
829 bool internExtraBindings(FrontendContext* fc, ParserAtomsTable& parserAtoms);
831 void trace(JSTracer* trc);
833 // Size of dynamic data. Note that GC data is counted by GC and not here. We
834 // also ignore ScriptSource which is a shared RefPtr.
835 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
836 return atomCache.sizeOfExcludingThis(mallocSizeOf);
838 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
839 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
842 #if defined(DEBUG) || defined(JS_JITSPEW)
843 void dump() const;
844 void dump(js::JSONPrinter& json) const;
845 void dumpFields(js::JSONPrinter& json) const;
846 #endif
849 // When compiling a function which was previously Syntaxly Parsed, we generated
850 // some information which made it possible to skip over some parsing phases,
851 // such as computing closed over bindings as well as parsing inner functions.
852 // This class contains all information which is generated by the SyntaxParse and
853 // reused in the FullParse.
854 class CompilationSyntaxParseCache {
855 // When delazifying, we should prepare an array which contains all
856 // stencil-like gc-things such that it can be used by the parser.
858 // When compiling from a Stencil, this will alias the existing Stencil.
859 mozilla::Span<TaggedScriptThingIndex> cachedGCThings_;
861 // When delazifying, we should perpare an array which contains all
862 // stencil-like information about scripts, such that it can be used by the
863 // parser.
865 // When compiling from a Stencil, these will alias the existing Stencil.
866 mozilla::Span<ScriptStencil> cachedScriptData_;
867 mozilla::Span<ScriptStencilExtra> cachedScriptExtra_;
869 // When delazifying, we copy the atom, either from JSAtom, or from another
870 // Stencil into TaggedParserAtomIndex which are valid in this current
871 // CompilationState.
872 mozilla::Span<TaggedParserAtomIndex> closedOverBindings_;
874 // Atom of the function being compiled. This atom index is valid in the
875 // current CompilationState.
876 TaggedParserAtomIndex displayAtom_;
878 // Stencil-like data about the function which is being compiled.
879 ScriptStencilExtra funExtra_;
881 #ifdef DEBUG
882 // Whether any of these data should be considered or not.
883 bool isInitialized = false;
884 #endif
886 public:
887 // When doing a full-parse of an incomplete BaseScript*, we have to iterate
888 // over functions and closed-over bindings, to avoid costly recursive decent
889 // in inner functions. This function will clone the BaseScript* information to
890 // make it available as a stencil-like data to the full-parser.
891 mozilla::Span<TaggedParserAtomIndex> closedOverBindings() const {
892 MOZ_ASSERT(isInitialized);
893 return closedOverBindings_;
895 const ScriptStencil& scriptData(size_t functionIndex) const {
896 return cachedScriptData_[scriptIndex(functionIndex)];
898 const ScriptStencilExtra& scriptExtra(size_t functionIndex) const {
899 return cachedScriptExtra_[scriptIndex(functionIndex)];
902 // Return the name of the function being delazified, if any.
903 TaggedParserAtomIndex displayAtom() const {
904 MOZ_ASSERT(isInitialized);
905 return displayAtom_;
908 // Return the extra information about the function being delazified, if any.
909 const ScriptStencilExtra& funExtra() const {
910 MOZ_ASSERT(isInitialized);
911 return funExtra_;
914 // Initialize the SynaxParse cache given a LifoAlloc. The JSContext is only
915 // used for reporting allocation errors.
916 [[nodiscard]] bool init(FrontendContext* fc, LifoAlloc& alloc,
917 ParserAtomsTable& parseAtoms,
918 CompilationAtomCache& atomCache,
919 const InputScript& lazy);
921 private:
922 // Return the script index of a given inner function.
924 // WARNING: The ScriptIndex returned by this function corresponds to the index
925 // in the cachedScriptExtra_ and cachedScriptData_ spans. With the
926 // cachedGCThings_ span, these might be reference to an actual Stencil from
927 // another compilation. Thus, the ScriptIndex returned by this function should
928 // not be confused with any ScriptIndex from the CompilationState.
929 ScriptIndex scriptIndex(size_t functionIndex) const {
930 MOZ_ASSERT(isInitialized);
931 auto taggedScriptIndex = cachedGCThings_[functionIndex];
932 MOZ_ASSERT(taggedScriptIndex.isFunction());
933 return taggedScriptIndex.toFunction();
936 [[nodiscard]] bool copyFunctionInfo(FrontendContext* fc,
937 ParserAtomsTable& parseAtoms,
938 CompilationAtomCache& atomCache,
939 const InputScript& lazy);
940 [[nodiscard]] bool copyScriptInfo(FrontendContext* fc, LifoAlloc& alloc,
941 ParserAtomsTable& parseAtoms,
942 CompilationAtomCache& atomCache,
943 BaseScript* lazy);
944 [[nodiscard]] bool copyScriptInfo(FrontendContext* fc, LifoAlloc& alloc,
945 ParserAtomsTable& parseAtoms,
946 CompilationAtomCache& atomCache,
947 const ScriptStencilRef& lazy);
948 [[nodiscard]] bool copyClosedOverBindings(FrontendContext* fc,
949 LifoAlloc& alloc,
950 ParserAtomsTable& parseAtoms,
951 CompilationAtomCache& atomCache,
952 BaseScript* lazy);
953 [[nodiscard]] bool copyClosedOverBindings(FrontendContext* fc,
954 LifoAlloc& alloc,
955 ParserAtomsTable& parseAtoms,
956 CompilationAtomCache& atomCache,
957 const ScriptStencilRef& lazy);
960 // AsmJS scripts are very rare on-average, so we use a HashMap to associate
961 // data with a ScriptStencil. The ScriptStencil has a flag to indicate if we
962 // need to even do this lookup.
963 using StencilAsmJSMap =
964 mozilla::HashMap<ScriptIndex, RefPtr<const JS::WasmModule>,
965 mozilla::DefaultHasher<ScriptIndex>,
966 js::SystemAllocPolicy>;
968 struct StencilAsmJSContainer
969 : public js::AtomicRefCounted<StencilAsmJSContainer> {
970 StencilAsmJSMap moduleMap;
972 StencilAsmJSContainer() = default;
974 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
975 return moduleMap.shallowSizeOfExcludingThis(mallocSizeOf);
977 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
978 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
982 // Store shared data for non-lazy script.
983 struct SharedDataContainer {
984 // NOTE: While stored, we must hold a ref-count and care must be taken when
985 // updating or clearing the pointer.
986 using SingleSharedDataPtr = SharedImmutableScriptData*;
988 using SharedDataVector =
989 Vector<RefPtr<js::SharedImmutableScriptData>, 0, js::SystemAllocPolicy>;
990 using SharedDataVectorPtr = SharedDataVector*;
992 using SharedDataMap =
993 mozilla::HashMap<ScriptIndex, RefPtr<js::SharedImmutableScriptData>,
994 mozilla::DefaultHasher<ScriptIndex>,
995 js::SystemAllocPolicy>;
996 using SharedDataMapPtr = SharedDataMap*;
998 private:
999 enum {
1000 SingleTag = 0,
1001 VectorTag = 1,
1002 MapTag = 2,
1003 BorrowTag = 3,
1005 TagMask = 3,
1008 uintptr_t data_ = 0;
1010 public:
1011 // Defaults to SingleSharedData.
1012 SharedDataContainer() = default;
1014 SharedDataContainer(const SharedDataContainer&) = delete;
1015 SharedDataContainer(SharedDataContainer&& other) noexcept {
1016 std::swap(data_, other.data_);
1017 MOZ_ASSERT(other.isEmpty());
1020 SharedDataContainer& operator=(const SharedDataContainer&) = delete;
1021 SharedDataContainer& operator=(SharedDataContainer&& other) noexcept {
1022 std::swap(data_, other.data_);
1023 MOZ_ASSERT(other.isEmpty());
1024 return *this;
1027 ~SharedDataContainer();
1029 [[nodiscard]] bool initVector(FrontendContext* fc);
1030 [[nodiscard]] bool initMap(FrontendContext* fc);
1032 private:
1033 [[nodiscard]] bool convertFromSingleToMap(FrontendContext* fc);
1035 public:
1036 bool isEmpty() const { return (data_) == SingleTag; }
1037 bool isSingle() const { return (data_ & TagMask) == SingleTag; }
1038 bool isVector() const { return (data_ & TagMask) == VectorTag; }
1039 bool isMap() const { return (data_ & TagMask) == MapTag; }
1040 bool isBorrow() const { return (data_ & TagMask) == BorrowTag; }
1042 void setSingle(already_AddRefed<SharedImmutableScriptData>&& data) {
1043 MOZ_ASSERT(isEmpty());
1044 data_ = reinterpret_cast<uintptr_t>(data.take());
1045 MOZ_ASSERT(isSingle());
1046 MOZ_ASSERT(!isEmpty());
1049 void setBorrow(SharedDataContainer* sharedData) {
1050 MOZ_ASSERT(isEmpty());
1051 data_ = reinterpret_cast<uintptr_t>(sharedData) | BorrowTag;
1052 MOZ_ASSERT(isBorrow());
1055 SingleSharedDataPtr asSingle() const {
1056 MOZ_ASSERT(isSingle());
1057 MOZ_ASSERT(!isEmpty());
1058 static_assert(SingleTag == 0);
1059 return reinterpret_cast<SingleSharedDataPtr>(data_);
1061 SharedDataVectorPtr asVector() const {
1062 MOZ_ASSERT(isVector());
1063 return reinterpret_cast<SharedDataVectorPtr>(data_ & ~TagMask);
1065 SharedDataMapPtr asMap() const {
1066 MOZ_ASSERT(isMap());
1067 return reinterpret_cast<SharedDataMapPtr>(data_ & ~TagMask);
1069 SharedDataContainer* asBorrow() const {
1070 MOZ_ASSERT(isBorrow());
1071 return reinterpret_cast<SharedDataContainer*>(data_ & ~TagMask);
1074 [[nodiscard]] bool prepareStorageFor(FrontendContext* fc,
1075 size_t nonLazyScriptCount,
1076 size_t allScriptCount);
1077 [[nodiscard]] bool cloneFrom(FrontendContext* fc,
1078 const SharedDataContainer& other);
1080 // Returns index-th script's shared data, or nullptr if it doesn't have.
1081 js::SharedImmutableScriptData* get(ScriptIndex index) const;
1083 // Add data for index-th script and share it with VM.
1084 [[nodiscard]] bool addAndShare(FrontendContext* fc, ScriptIndex index,
1085 js::SharedImmutableScriptData* data);
1087 // Add data for index-th script without sharing it with VM.
1088 // The data should already be shared with VM.
1090 // The data is supposed to be added from delazification.
1091 [[nodiscard]] bool addExtraWithoutShare(FrontendContext* fc,
1092 ScriptIndex index,
1093 js::SharedImmutableScriptData* data);
1095 // Dynamic memory associated with this container. Does not include the
1096 // SharedImmutableScriptData since we are not the unique owner of it.
1097 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
1098 if (isVector()) {
1099 return asVector()->sizeOfIncludingThis(mallocSizeOf);
1101 if (isMap()) {
1102 return asMap()->shallowSizeOfIncludingThis(mallocSizeOf);
1104 MOZ_ASSERT(isSingle() || isBorrow());
1105 return 0;
1108 #if defined(DEBUG) || defined(JS_JITSPEW)
1109 void dump() const;
1110 void dump(js::JSONPrinter& json) const;
1111 void dumpFields(js::JSONPrinter& json) const;
1112 #endif
1115 struct ExtensibleCompilationStencil;
1117 // The top level struct of stencil specialized for non-extensible case.
1118 // Used as the compilation output, and also XDR decode output.
1120 // In XDR decode output case, the span and not-owning pointer fields point
1121 // the internal LifoAlloc and the external XDR buffer.
1123 // In BorrowingCompilationStencil usage, span and not-owning pointer fields
1124 // point the ExtensibleCompilationStencil and its LifoAlloc.
1126 // The dependent XDR buffer or ExtensibleCompilationStencil must be kept
1127 // alive manually.
1129 // See SMDOC in Stencil.h for more info.
1130 struct CompilationStencil {
1131 friend struct ExtensibleCompilationStencil;
1133 static constexpr ScriptIndex TopLevelIndex = ScriptIndex(0);
1135 static constexpr size_t LifoAllocChunkSize = 512;
1137 // The lifetime of this CompilationStencil may be managed by stack allocation,
1138 // UniquePtr<T>, or RefPtr<T>. If a RefPtr is used, this ref-count will track
1139 // the lifetime, otherwise it is ignored.
1141 // NOTE: Internal code and public APIs use a mix of these different allocation
1142 // modes.
1144 // See: JS::StencilAddRef/Release
1145 mutable mozilla::Atomic<uintptr_t> refCount{0};
1147 private:
1148 // On-heap ExtensibleCompilationStencil that this CompilationStencil owns,
1149 // and this CompilationStencil borrows each data from.
1150 UniquePtr<ExtensibleCompilationStencil> ownedBorrowStencil;
1152 public:
1153 enum class StorageType {
1154 // Pointers and spans point LifoAlloc or owned buffer.
1155 Owned,
1157 // Pointers and spans point external data, such as XDR buffer, or not-owned
1158 // ExtensibleCompilationStencil (see BorrowingCompilationStencil).
1159 Borrowed,
1161 // Pointers and spans point data owned by ownedBorrowStencil.
1162 OwnedExtensible,
1164 StorageType storageType = StorageType::Owned;
1166 // Value of CanLazilyParse(CompilationInput) on compilation.
1167 // Used during instantiation.
1168 bool canLazilyParse = false;
1170 // If this stencil is a delazification, this identifies location of the
1171 // function in the source text.
1172 using FunctionKey = SourceExtent::FunctionKey;
1173 FunctionKey functionKey = SourceExtent::NullFunctionKey;
1175 // This holds allocations that do not require destructors to be run but are
1176 // live until the stencil is released.
1177 LifoAlloc alloc;
1179 // The source text holder for the script. This may be an empty placeholder if
1180 // the code will fully parsed and options indicate the source will never be
1181 // needed again.
1182 RefPtr<ScriptSource> source;
1184 // Stencil for all function and non-function scripts. The TopLevelIndex is
1185 // reserved for the top-level script. This top-level may or may not be a
1186 // function.
1187 mozilla::Span<ScriptStencil> scriptData;
1189 // Immutable data computed during initial compilation and never updated during
1190 // delazification.
1191 mozilla::Span<ScriptStencilExtra> scriptExtra;
1193 mozilla::Span<TaggedScriptThingIndex> gcThingData;
1195 // scopeData and scopeNames have the same size, and i-th scopeNames contains
1196 // the names for the bindings contained in the slot defined by i-th scopeData.
1197 mozilla::Span<ScopeStencil> scopeData;
1198 mozilla::Span<BaseParserScopeData*> scopeNames;
1200 // Hold onto the RegExpStencil, BigIntStencil, and ObjLiteralStencil that are
1201 // allocated during parse to ensure correct destruction.
1202 mozilla::Span<RegExpStencil> regExpData;
1203 mozilla::Span<BigIntStencil> bigIntData;
1204 mozilla::Span<ObjLiteralStencil> objLiteralData;
1206 // List of parser atoms for this compilation. This may contain nullptr entries
1207 // when round-tripping with XDR if the atom was generated in original parse
1208 // but not used by stencil.
1209 ParserAtomSpan parserAtomData;
1211 // Variable sized container for bytecode and other immutable data. A valid
1212 // stencil always contains at least an entry for `TopLevelIndex` script.
1213 SharedDataContainer sharedData;
1215 // Module metadata if this is a module compile.
1216 RefPtr<StencilModuleMetadata> moduleMetadata;
1218 // AsmJS modules generated by parsing. These scripts are never lazy and
1219 // therefore only generated during initial parse.
1220 RefPtr<StencilAsmJSContainer> asmJS;
1222 // End of fields.
1224 // Construct a CompilationStencil
1225 explicit CompilationStencil(ScriptSource* source)
1226 : alloc(LifoAllocChunkSize), source(source) {}
1228 // Take the ownership of on-heap ExtensibleCompilationStencil and
1229 // borrow from it.
1230 explicit CompilationStencil(
1231 UniquePtr<ExtensibleCompilationStencil>&& extensibleStencil);
1233 protected:
1234 void borrowFromExtensibleCompilationStencil(
1235 ExtensibleCompilationStencil& extensibleStencil);
1237 #ifdef DEBUG
1238 void assertBorrowingFromExtensibleCompilationStencil(
1239 const ExtensibleCompilationStencil& extensibleStencil) const;
1240 #endif
1242 public:
1243 bool isInitialStencil() const {
1244 return functionKey == SourceExtent::NullFunctionKey;
1247 [[nodiscard]] static bool instantiateStencilAfterPreparation(
1248 JSContext* cx, CompilationInput& input, const CompilationStencil& stencil,
1249 CompilationGCOutput& gcOutput);
1251 [[nodiscard]] static bool prepareForInstantiate(
1252 FrontendContext* fc, CompilationAtomCache& atomCache,
1253 const CompilationStencil& stencil, CompilationGCOutput& gcOutput);
1254 [[nodiscard]] static bool prepareForInstantiate(
1255 FrontendContext* fc, const CompilationStencil& stencil,
1256 PreallocatedCompilationGCOutput& gcOutput);
1258 [[nodiscard]] static bool instantiateStencils(
1259 JSContext* cx, CompilationInput& input, const CompilationStencil& stencil,
1260 CompilationGCOutput& gcOutput);
1262 // Decode the special self-hosted stencil
1263 [[nodiscard]] bool instantiateSelfHostedAtoms(
1264 JSContext* cx, AtomSet& atomSet, CompilationAtomCache& atomCache) const;
1265 [[nodiscard]] JSScript* instantiateSelfHostedTopLevelForRealm(
1266 JSContext* cx, CompilationInput& input);
1267 [[nodiscard]] JSFunction* instantiateSelfHostedLazyFunction(
1268 JSContext* cx, CompilationAtomCache& atomCache, ScriptIndex index,
1269 JS::Handle<JSAtom*> name);
1270 [[nodiscard]] bool delazifySelfHostedFunction(JSContext* cx,
1271 CompilationAtomCache& atomCache,
1272 ScriptIndexRange range,
1273 JS::Handle<JSFunction*> fun);
1275 [[nodiscard]] bool serializeStencils(JSContext* cx, CompilationInput& input,
1276 JS::TranscodeBuffer& buf,
1277 bool* succeededOut = nullptr) const;
1278 [[nodiscard]] bool deserializeStencils(
1279 FrontendContext* fc, const JS::ReadOnlyCompileOptions& options,
1280 const JS::TranscodeRange& range, bool* succeededOut = nullptr);
1282 // To avoid any misuses, make sure this is neither copyable or assignable.
1283 CompilationStencil(const CompilationStencil&) = delete;
1284 CompilationStencil(CompilationStencil&&) = delete;
1285 CompilationStencil& operator=(const CompilationStencil&) = delete;
1286 CompilationStencil& operator=(CompilationStencil&&) = delete;
1287 #ifdef DEBUG
1288 ~CompilationStencil() {
1289 // We can mix UniquePtr<..> and RefPtr<..>. This asserts that a UniquePtr
1290 // does not delete a reference-counted stencil.
1291 MOZ_ASSERT(!refCount);
1293 #endif
1295 static inline ScriptStencilIterable functionScriptStencils(
1296 const CompilationStencil& stencil, CompilationGCOutput& gcOutput);
1298 void setFunctionKey(BaseScript* lazy) {
1299 functionKey = lazy->extent().toFunctionKey();
1302 inline size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
1303 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
1304 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
1307 const ParserAtomSpan& parserAtomsSpan() const { return parserAtomData; }
1309 bool isModule() const;
1311 bool hasMultipleReference() const { return refCount > 1; }
1313 bool hasOwnedBorrow() const {
1314 return storageType == StorageType::OwnedExtensible;
1317 ExtensibleCompilationStencil* takeOwnedBorrow() {
1318 MOZ_ASSERT(!hasMultipleReference());
1319 MOZ_ASSERT(hasOwnedBorrow());
1320 return ownedBorrowStencil.release();
1323 #ifdef DEBUG
1324 void assertNoExternalDependency() const;
1325 #endif
1327 #if defined(DEBUG) || defined(JS_JITSPEW)
1328 void dump() const;
1329 void dump(js::JSONPrinter& json) const;
1330 void dumpFields(js::JSONPrinter& json) const;
1332 void dumpAtom(TaggedParserAtomIndex index) const;
1333 #endif
1336 // The top level struct of stencil specialized for extensible case.
1337 // Used as the temporary storage during compilation, an the compilation output.
1339 // All not-owning pointer fields point the internal LifoAlloc.
1341 // See CompilationStencil for each field's description.
1343 // Also see SMDOC in Stencil.h for more info.
1344 struct ExtensibleCompilationStencil {
1345 bool canLazilyParse = false;
1347 using FunctionKey = SourceExtent::FunctionKey;
1349 FunctionKey functionKey = SourceExtent::NullFunctionKey;
1351 // Data pointed by other fields are allocated in this LifoAlloc,
1352 // and moved to `CompilationStencil.alloc`.
1353 LifoAlloc alloc;
1355 RefPtr<ScriptSource> source;
1357 // NOTE: We reserve a modest amount of inline storage in order to reduce
1358 // allocations in the most common delazification cases. These common
1359 // cases have one script and scope, as well as a handful of gcthings.
1360 // For complex pages this covers about 75% of delazifications.
1362 Vector<ScriptStencil, 1, js::SystemAllocPolicy> scriptData;
1363 Vector<ScriptStencilExtra, 0, js::SystemAllocPolicy> scriptExtra;
1365 Vector<TaggedScriptThingIndex, 8, js::SystemAllocPolicy> gcThingData;
1367 Vector<ScopeStencil, 1, js::SystemAllocPolicy> scopeData;
1368 Vector<BaseParserScopeData*, 1, js::SystemAllocPolicy> scopeNames;
1370 Vector<RegExpStencil, 0, js::SystemAllocPolicy> regExpData;
1371 Vector<BigIntStencil, 0, js::SystemAllocPolicy> bigIntData;
1372 Vector<ObjLiteralStencil, 0, js::SystemAllocPolicy> objLiteralData;
1374 // Table of parser atoms for this compilation.
1375 ParserAtomsTable parserAtoms;
1377 SharedDataContainer sharedData;
1379 RefPtr<StencilModuleMetadata> moduleMetadata;
1381 RefPtr<StencilAsmJSContainer> asmJS;
1383 explicit ExtensibleCompilationStencil(ScriptSource* source);
1385 explicit ExtensibleCompilationStencil(CompilationInput& input);
1386 ExtensibleCompilationStencil(const JS::ReadOnlyCompileOptions& options,
1387 RefPtr<ScriptSource> source);
1389 ExtensibleCompilationStencil(ExtensibleCompilationStencil&& other) noexcept
1390 : canLazilyParse(other.canLazilyParse),
1391 functionKey(other.functionKey),
1392 alloc(CompilationStencil::LifoAllocChunkSize),
1393 source(std::move(other.source)),
1394 scriptData(std::move(other.scriptData)),
1395 scriptExtra(std::move(other.scriptExtra)),
1396 gcThingData(std::move(other.gcThingData)),
1397 scopeData(std::move(other.scopeData)),
1398 scopeNames(std::move(other.scopeNames)),
1399 regExpData(std::move(other.regExpData)),
1400 bigIntData(std::move(other.bigIntData)),
1401 objLiteralData(std::move(other.objLiteralData)),
1402 parserAtoms(std::move(other.parserAtoms)),
1403 sharedData(std::move(other.sharedData)),
1404 moduleMetadata(std::move(other.moduleMetadata)),
1405 asmJS(std::move(other.asmJS)) {
1406 alloc.steal(&other.alloc);
1407 parserAtoms.fixupAlloc(alloc);
1410 ExtensibleCompilationStencil& operator=(
1411 ExtensibleCompilationStencil&& other) noexcept {
1412 MOZ_ASSERT(alloc.isEmpty());
1414 canLazilyParse = other.canLazilyParse;
1415 functionKey = other.functionKey;
1416 source = std::move(other.source);
1417 scriptData = std::move(other.scriptData);
1418 scriptExtra = std::move(other.scriptExtra);
1419 gcThingData = std::move(other.gcThingData);
1420 scopeData = std::move(other.scopeData);
1421 scopeNames = std::move(other.scopeNames);
1422 regExpData = std::move(other.regExpData);
1423 bigIntData = std::move(other.bigIntData);
1424 objLiteralData = std::move(other.objLiteralData);
1425 parserAtoms = std::move(other.parserAtoms);
1426 sharedData = std::move(other.sharedData);
1427 moduleMetadata = std::move(other.moduleMetadata);
1428 asmJS = std::move(other.asmJS);
1430 alloc.steal(&other.alloc);
1431 parserAtoms.fixupAlloc(alloc);
1433 return *this;
1436 void setFunctionKey(const SourceExtent& extent) {
1437 functionKey = extent.toFunctionKey();
1440 bool isInitialStencil() const {
1441 return functionKey == SourceExtent::NullFunctionKey;
1444 // Steal CompilationStencil content.
1445 [[nodiscard]] bool steal(FrontendContext* fc,
1446 RefPtr<CompilationStencil>&& other);
1448 // Clone ExtensibleCompilationStencil content.
1449 [[nodiscard]] bool cloneFrom(FrontendContext* fc,
1450 const CompilationStencil& other);
1451 [[nodiscard]] bool cloneFrom(FrontendContext* fc,
1452 const ExtensibleCompilationStencil& other);
1454 private:
1455 template <typename Stencil>
1456 [[nodiscard]] bool cloneFromImpl(FrontendContext* fc, const Stencil& other);
1458 public:
1459 const ParserAtomVector& parserAtomsSpan() const {
1460 return parserAtoms.entries();
1463 bool isModule() const;
1465 inline size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
1466 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
1467 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
1470 #ifdef DEBUG
1471 void assertNoExternalDependency() const;
1472 #endif
1474 #if defined(DEBUG) || defined(JS_JITSPEW)
1475 void dump();
1476 void dump(js::JSONPrinter& json);
1477 void dumpFields(js::JSONPrinter& json);
1479 void dumpAtom(TaggedParserAtomIndex index);
1480 #endif
1483 // The internal state of the compilation.
1484 struct MOZ_RAII CompilationState : public ExtensibleCompilationStencil {
1485 Directives directives;
1487 ScopeContext scopeContext;
1489 UsedNameTracker usedNames;
1491 // LifoAlloc scope used by Parser for allocating AST etc.
1493 // NOTE: This is not used for ExtensibleCompilationStencil.alloc.
1494 LifoAllocScope& parserAllocScope;
1496 CompilationInput& input;
1497 CompilationSyntaxParseCache previousParseCache;
1499 // The number of functions that *will* have bytecode.
1500 // This doesn't count top-level non-function script.
1502 // This should be counted while parsing, and should be passed to
1503 // SharedDataContainer.prepareStorageFor *before* start emitting bytecode.
1504 size_t nonLazyFunctionCount = 0;
1506 // End of fields.
1508 CompilationState(FrontendContext* fc, LifoAllocScope& parserAllocScope,
1509 CompilationInput& input);
1511 bool init(FrontendContext* fc, ScopeBindingCache* scopeCache,
1512 InheritThis inheritThis = InheritThis::No,
1513 JSObject* enclosingEnv = nullptr) {
1514 if (!scopeContext.init(fc, input, parserAtoms, scopeCache, inheritThis,
1515 enclosingEnv)) {
1516 return false;
1519 // gcThings is later used by the full parser initialization.
1520 if (input.isDelazifying()) {
1521 InputScript lazy = input.lazyOuterScript();
1522 auto& atomCache = input.atomCache;
1523 if (!previousParseCache.init(fc, alloc, parserAtoms, atomCache, lazy)) {
1524 return false;
1528 return true;
1531 // Track the state of key allocations and roll them back as parts of parsing
1532 // get retried. This ensures iteration during stencil instantiation does not
1533 // encounter discarded frontend state.
1534 struct CompilationStatePosition {
1535 // Temporarily share this token struct with CompilationState.
1536 size_t scriptDataLength = 0;
1538 size_t asmJSCount = 0;
1541 bool prepareSharedDataStorage(FrontendContext* fc);
1543 CompilationStatePosition getPosition();
1544 void rewind(const CompilationStatePosition& pos);
1546 // When parsing arrow function, parameter is parsed twice, and if there are
1547 // functions inside parameter expression, stencils will be created for them.
1549 // Those functions exist only for lazy parsing.
1550 // Mark them "ghost", so that they don't affect other parts.
1552 // See GHOST_FUNCTION in FunctionFlags.h for more details.
1553 void markGhost(const CompilationStatePosition& pos);
1555 // Allocate space for `length` gcthings, and return the address of the
1556 // first element to `cursor` to initialize on the caller.
1557 bool allocateGCThingsUninitialized(FrontendContext* fc,
1558 ScriptIndex scriptIndex, size_t length,
1559 TaggedScriptThingIndex** cursor);
1561 bool appendScriptStencilAndData(FrontendContext* fc);
1563 bool appendGCThings(FrontendContext* fc, ScriptIndex scriptIndex,
1564 mozilla::Span<const TaggedScriptThingIndex> things);
1567 // A temporary CompilationStencil instance that borrows
1568 // ExtensibleCompilationStencil data.
1569 // Ensure that this instance does not outlive the ExtensibleCompilationStencil.
1570 class MOZ_STACK_CLASS BorrowingCompilationStencil : public CompilationStencil {
1571 public:
1572 explicit BorrowingCompilationStencil(
1573 ExtensibleCompilationStencil& extensibleStencil);
1576 // Size of dynamic data. Ignores Spans (unless their contents are in the
1577 // LifoAlloc) and RefPtrs since we are not the unique owner.
1578 inline size_t CompilationStencil::sizeOfExcludingThis(
1579 mozilla::MallocSizeOf mallocSizeOf) const {
1580 if (ownedBorrowStencil) {
1581 return ownedBorrowStencil->sizeOfIncludingThis(mallocSizeOf);
1584 size_t moduleMetadataSize =
1585 moduleMetadata ? moduleMetadata->sizeOfIncludingThis(mallocSizeOf) : 0;
1586 size_t asmJSSize = asmJS ? asmJS->sizeOfIncludingThis(mallocSizeOf) : 0;
1588 return alloc.sizeOfExcludingThis(mallocSizeOf) +
1589 sharedData.sizeOfExcludingThis(mallocSizeOf) + moduleMetadataSize +
1590 asmJSSize;
1593 inline size_t ExtensibleCompilationStencil::sizeOfExcludingThis(
1594 mozilla::MallocSizeOf mallocSizeOf) const {
1595 size_t moduleMetadataSize =
1596 moduleMetadata ? moduleMetadata->sizeOfIncludingThis(mallocSizeOf) : 0;
1597 size_t asmJSSize = asmJS ? asmJS->sizeOfIncludingThis(mallocSizeOf) : 0;
1599 return alloc.sizeOfExcludingThis(mallocSizeOf) +
1600 scriptData.sizeOfExcludingThis(mallocSizeOf) +
1601 scriptExtra.sizeOfExcludingThis(mallocSizeOf) +
1602 gcThingData.sizeOfExcludingThis(mallocSizeOf) +
1603 scopeData.sizeOfExcludingThis(mallocSizeOf) +
1604 scopeNames.sizeOfExcludingThis(mallocSizeOf) +
1605 regExpData.sizeOfExcludingThis(mallocSizeOf) +
1606 bigIntData.sizeOfExcludingThis(mallocSizeOf) +
1607 objLiteralData.sizeOfExcludingThis(mallocSizeOf) +
1608 parserAtoms.sizeOfExcludingThis(mallocSizeOf) +
1609 sharedData.sizeOfExcludingThis(mallocSizeOf) + moduleMetadataSize +
1610 asmJSSize;
1613 // A PreAllocateableGCArray is an array of GC thing pointers.
1615 // The array's internal buffer can be allocated ahead of time, possibly off
1616 // main thread.
1617 template <typename T>
1618 struct PreAllocateableGCArray {
1619 private:
1620 size_t length_ = 0;
1622 // Inline element for the case when length_ == 1.
1623 T inlineElem_;
1625 // Heap-allocated elements for the case when length_ > 1;
1626 T* elems_ = nullptr;
1628 public:
1629 struct Preallocated {
1630 private:
1631 size_t length_ = 0;
1632 uintptr_t* elems_ = nullptr;
1634 friend struct PreAllocateableGCArray<T>;
1636 public:
1637 Preallocated() = default;
1638 ~Preallocated();
1640 bool empty() const { return length_ == 0; }
1642 size_t length() const { return length_; }
1644 private:
1645 bool isInline() const { return length_ == 1; }
1647 public:
1648 bool allocate(size_t length);
1650 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
1651 return sizeof(uintptr_t) * length_;
1655 PreAllocateableGCArray() {
1656 static_assert(std::is_pointer_v<T>,
1657 "PreAllocateableGCArray element must be a pointer");
1659 ~PreAllocateableGCArray();
1661 bool empty() const { return length_ == 0; }
1663 size_t length() const { return length_; }
1665 private:
1666 bool isInline() const { return length_ == 1; }
1668 public:
1669 bool allocate(size_t length);
1670 bool allocateWith(T init, size_t length);
1672 // Steal pre-allocated buffer.
1673 void steal(Preallocated&& buffer);
1675 T& operator[](size_t index) {
1676 MOZ_ASSERT(index < length_);
1678 if (isInline()) {
1679 return inlineElem_;
1682 return elems_[index];
1684 const T& operator[](size_t index) const {
1685 MOZ_ASSERT(index < length_);
1687 if (isInline()) {
1688 return inlineElem_;
1691 return elems_[index];
1694 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
1695 if (!elems_) {
1696 return 0;
1699 return sizeof(T) * length_;
1702 void trace(JSTracer* trc);
1705 struct CompilationGCOutput;
1707 // Pre-allocated storage for CompilationGCOutput.
1708 struct PreallocatedCompilationGCOutput {
1709 private:
1710 PreAllocateableGCArray<JSFunction*>::Preallocated functions;
1711 PreAllocateableGCArray<js::Scope*>::Preallocated scopes;
1713 friend struct CompilationGCOutput;
1715 public:
1716 PreallocatedCompilationGCOutput() = default;
1718 [[nodiscard]] bool allocate(FrontendContext* fc, size_t scriptDataLength,
1719 size_t scopeDataLength) {
1720 if (!functions.allocate(scriptDataLength)) {
1721 ReportOutOfMemory(fc);
1722 return false;
1724 if (!scopes.allocate(scopeDataLength)) {
1725 ReportOutOfMemory(fc);
1726 return false;
1728 return true;
1731 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
1732 return functions.sizeOfExcludingThis(mallocSizeOf) +
1733 scopes.sizeOfExcludingThis(mallocSizeOf);
1737 // The output of GC allocation from stencil.
1738 struct CompilationGCOutput {
1739 // The resulting outermost script for the compilation powered
1740 // by this CompilationStencil.
1741 JSScript* script = nullptr;
1743 // The resulting module object if there is one.
1744 ModuleObject* module = nullptr;
1746 // An array to handle tracing of JSFunction* and Atoms within.
1748 // If the top level script isn't a function, the item at TopLevelIndex is
1749 // nullptr.
1750 PreAllocateableGCArray<JSFunction*> functions;
1752 // References to scopes are controlled via AbstractScopePtr, which holds onto
1753 // an index (and CompilationStencil reference).
1754 PreAllocateableGCArray<js::Scope*> scopes;
1756 // The result ScriptSourceObject. This is unused in delazifying parses.
1757 ScriptSourceObject* sourceObject = nullptr;
1759 private:
1760 // If we are only instantiating part of a stencil, we can reduce allocations
1761 // by setting a base index and allocating only the array elements we need.
1762 // This applies to both the `functions` and `scopes` arrays. These fields are
1763 // initialized by `ensureAllocatedWithBaseIndex` which also allocates the
1764 // array appropriately.
1766 // Note: These are only used for self-hosted delazification currently.
1767 ScriptIndex functionsBaseIndex{};
1768 ScopeIndex scopesBaseIndex{};
1770 // End of fields.
1772 public:
1773 CompilationGCOutput() = default;
1775 // Helper to access the `functions` array. The NoBaseIndex version is used if
1776 // the caller never uses a base index.
1777 JSFunction*& getFunction(ScriptIndex index) {
1778 return functions[index - functionsBaseIndex];
1780 JSFunction*& getFunctionNoBaseIndex(ScriptIndex index) {
1781 MOZ_ASSERT(!functionsBaseIndex);
1782 return functions[index];
1785 // Helper accessors for the `scopes` array.
1786 js::Scope*& getScope(ScopeIndex index) {
1787 return scopes[index - scopesBaseIndex];
1789 js::Scope*& getScopeNoBaseIndex(ScopeIndex index) {
1790 MOZ_ASSERT(!scopesBaseIndex);
1791 return scopes[index];
1793 js::Scope* getScopeNoBaseIndex(ScopeIndex index) const {
1794 MOZ_ASSERT(!scopesBaseIndex);
1795 return scopes[index];
1798 // Allocate output arrays.
1799 [[nodiscard]] bool ensureAllocated(FrontendContext* fc,
1800 size_t scriptDataLength,
1801 size_t scopeDataLength) {
1802 if (functions.empty()) {
1803 if (!functions.allocate(scriptDataLength)) {
1804 ReportOutOfMemory(fc);
1805 return false;
1808 if (scopes.empty()) {
1809 if (!scopes.allocate(scopeDataLength)) {
1810 ReportOutOfMemory(fc);
1811 return false;
1814 return true;
1817 // Steal output arrays' buffer.
1818 void steal(PreallocatedCompilationGCOutput&& pre) {
1819 functions.steal(std::move(pre.functions));
1820 scopes.steal(std::move(pre.scopes));
1823 // A variant of `ensureAllocated` that sets a base index for the function and
1824 // scope arrays. This is used when instantiating only a subset of the stencil.
1825 // Currently this only applies to self-hosted delazification. The ranges
1826 // include the start index and exclude the limit index.
1827 [[nodiscard]] bool ensureAllocatedWithBaseIndex(FrontendContext* fc,
1828 ScriptIndex scriptStart,
1829 ScriptIndex scriptLimit,
1830 ScopeIndex scopeStart,
1831 ScopeIndex scopeLimit) {
1832 this->functionsBaseIndex = scriptStart;
1833 this->scopesBaseIndex = scopeStart;
1835 return ensureAllocated(fc, scriptLimit - scriptStart,
1836 scopeLimit - scopeStart);
1839 // Size of dynamic data. Note that GC data is counted by GC and not here.
1840 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
1841 return functions.sizeOfExcludingThis(mallocSizeOf) +
1842 scopes.sizeOfExcludingThis(mallocSizeOf);
1845 void trace(JSTracer* trc);
1848 // Iterator over functions that make up a CompilationStencil. This abstracts
1849 // over the parallel arrays in stencil and gc-output that use the same index
1850 // system.
1851 class ScriptStencilIterable {
1852 public:
1853 class ScriptAndFunction {
1854 public:
1855 const ScriptStencil& script;
1856 const ScriptStencilExtra* scriptExtra;
1857 JSFunction* function;
1858 ScriptIndex index;
1860 ScriptAndFunction() = delete;
1861 ScriptAndFunction(const ScriptStencil& script,
1862 const ScriptStencilExtra* scriptExtra,
1863 JSFunction* function, ScriptIndex index)
1864 : script(script),
1865 scriptExtra(scriptExtra),
1866 function(function),
1867 index(index) {}
1870 class Iterator {
1871 size_t index_ = 0;
1872 const CompilationStencil& stencil_;
1873 CompilationGCOutput& gcOutput_;
1875 Iterator(const CompilationStencil& stencil, CompilationGCOutput& gcOutput,
1876 size_t index)
1877 : index_(index), stencil_(stencil), gcOutput_(gcOutput) {
1878 MOZ_ASSERT(index == stencil.scriptData.size());
1881 public:
1882 explicit Iterator(const CompilationStencil& stencil,
1883 CompilationGCOutput& gcOutput)
1884 : stencil_(stencil), gcOutput_(gcOutput) {
1885 skipTopLevelNonFunction();
1888 Iterator operator++() {
1889 next();
1890 assertFunction();
1891 return *this;
1894 void next() {
1895 MOZ_ASSERT(index_ < stencil_.scriptData.size());
1896 index_++;
1899 void assertFunction() {
1900 if (index_ < stencil_.scriptData.size()) {
1901 MOZ_ASSERT(stencil_.scriptData[index_].isFunction());
1905 void skipTopLevelNonFunction() {
1906 MOZ_ASSERT(index_ == 0);
1907 if (stencil_.scriptData.size()) {
1908 if (!stencil_.scriptData[0].isFunction()) {
1909 next();
1910 assertFunction();
1915 bool operator!=(const Iterator& other) const {
1916 return index_ != other.index_;
1919 ScriptAndFunction operator*() {
1920 ScriptIndex index = ScriptIndex(index_);
1921 const ScriptStencil& script = stencil_.scriptData[index];
1922 const ScriptStencilExtra* scriptExtra = nullptr;
1923 if (stencil_.isInitialStencil()) {
1924 scriptExtra = &stencil_.scriptExtra[index];
1926 return ScriptAndFunction(script, scriptExtra,
1927 gcOutput_.getFunctionNoBaseIndex(index), index);
1930 static Iterator end(const CompilationStencil& stencil,
1931 CompilationGCOutput& gcOutput) {
1932 return Iterator(stencil, gcOutput, stencil.scriptData.size());
1936 const CompilationStencil& stencil_;
1937 CompilationGCOutput& gcOutput_;
1939 explicit ScriptStencilIterable(const CompilationStencil& stencil,
1940 CompilationGCOutput& gcOutput)
1941 : stencil_(stencil), gcOutput_(gcOutput) {}
1943 Iterator begin() const { return Iterator(stencil_, gcOutput_); }
1945 Iterator end() const { return Iterator::end(stencil_, gcOutput_); }
1948 inline ScriptStencilIterable CompilationStencil::functionScriptStencils(
1949 const CompilationStencil& stencil, CompilationGCOutput& gcOutput) {
1950 return ScriptStencilIterable(stencil, gcOutput);
1953 // Merge CompilationStencil for delazification into initial
1954 // ExtensibleCompilationStencil.
1955 struct CompilationStencilMerger {
1956 private:
1957 using FunctionKey = SourceExtent::FunctionKey;
1959 // The stencil for the initial compilation.
1960 // Delazifications are merged into this.
1962 // If any failure happens during merge operation, this field is reset to
1963 // nullptr.
1964 UniquePtr<ExtensibleCompilationStencil> initial_;
1966 // A Map from function key to the ScriptIndex in the initial stencil.
1967 using FunctionKeyToScriptIndexMap =
1968 mozilla::HashMap<FunctionKey, ScriptIndex,
1969 mozilla::DefaultHasher<FunctionKey>,
1970 js::SystemAllocPolicy>;
1971 FunctionKeyToScriptIndexMap functionKeyToInitialScriptIndex_;
1973 [[nodiscard]] bool buildFunctionKeyToIndex(FrontendContext* fc);
1975 ScriptIndex getInitialScriptIndexFor(
1976 const CompilationStencil& delazification) const;
1978 // A map from delazification's ParserAtomIndex to
1979 // initial's TaggedParserAtomIndex
1980 using AtomIndexMap = Vector<TaggedParserAtomIndex, 0, js::SystemAllocPolicy>;
1982 [[nodiscard]] bool buildAtomIndexMap(FrontendContext* fc,
1983 const CompilationStencil& delazification,
1984 AtomIndexMap& atomIndexMap);
1986 public:
1987 CompilationStencilMerger() = default;
1989 // Set the initial stencil and prepare for merging.
1990 [[nodiscard]] bool setInitial(
1991 FrontendContext* fc, UniquePtr<ExtensibleCompilationStencil>&& initial);
1993 // Merge the delazification stencil into the initial stencil.
1994 [[nodiscard]] bool addDelazification(
1995 FrontendContext* fc, const CompilationStencil& delazification);
1997 ExtensibleCompilationStencil& getResult() const { return *initial_; }
1998 UniquePtr<ExtensibleCompilationStencil> takeResult() {
1999 return std::move(initial_);
2003 const ScopeStencil& ScopeStencilRef::scope() const {
2004 return context_.scopeData[scopeIndex_];
2007 InputScope InputScope::enclosing() const {
2008 return scope_.match(
2009 [](const Scope* ptr) {
2010 // This may return a nullptr Scope pointer.
2011 return InputScope(ptr->enclosing());
2013 [](const ScopeStencilRef& ref) {
2014 if (ref.scope().hasEnclosing()) {
2015 return InputScope(ref.context_, ref.scope().enclosing());
2017 // The global scope is not known by the Stencil, while parsing inner
2018 // functions from Stencils where they are known at the execution using
2019 // the GlobalScope.
2020 if (ref.scope().kind() == ScopeKind::Module) {
2021 return InputScope(FakeStencilGlobalScope{});
2023 return InputScope(nullptr);
2025 [](const FakeStencilGlobalScope&) { return InputScope(nullptr); });
2028 FunctionFlags InputScope::functionFlags() const {
2029 return scope_.match(
2030 [](const Scope* ptr) {
2031 JSFunction* fun = ptr->as<FunctionScope>().canonicalFunction();
2032 return fun->flags();
2034 [](const ScopeStencilRef& ref) {
2035 MOZ_ASSERT(ref.scope().isFunction());
2036 ScriptIndex scriptIndex = ref.scope().functionIndex();
2037 ScriptStencil& data = ref.context_.scriptData[scriptIndex];
2038 return data.functionFlags;
2040 [](const FakeStencilGlobalScope&) -> FunctionFlags {
2041 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No functionFlags on global.");
2045 ImmutableScriptFlags InputScope::immutableFlags() const {
2046 return scope_.match(
2047 [](const Scope* ptr) {
2048 JSFunction* fun = ptr->as<FunctionScope>().canonicalFunction();
2049 return fun->baseScript()->immutableFlags();
2051 [](const ScopeStencilRef& ref) {
2052 MOZ_ASSERT(ref.scope().isFunction());
2053 ScriptIndex scriptIndex = ref.scope().functionIndex();
2054 ScriptStencilExtra& extra = ref.context_.scriptExtra[scriptIndex];
2055 return extra.immutableFlags;
2057 [](const FakeStencilGlobalScope&) -> ImmutableScriptFlags {
2058 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No immutableFlags on global.");
2062 MemberInitializers InputScope::getMemberInitializers() const {
2063 return scope_.match(
2064 [](const Scope* ptr) {
2065 JSFunction* fun = ptr->as<FunctionScope>().canonicalFunction();
2066 return fun->baseScript()->getMemberInitializers();
2068 [](const ScopeStencilRef& ref) {
2069 MOZ_ASSERT(ref.scope().isFunction());
2070 ScriptIndex scriptIndex = ref.scope().functionIndex();
2071 ScriptStencilExtra& extra = ref.context_.scriptExtra[scriptIndex];
2072 return extra.memberInitializers();
2074 [](const FakeStencilGlobalScope&) -> MemberInitializers {
2075 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
2076 "No getMemberInitializers on global.");
2080 const ScriptStencil& ScriptStencilRef::scriptData() const {
2081 return context_.scriptData[scriptIndex_];
2084 const ScriptStencilExtra& ScriptStencilRef::scriptExtra() const {
2085 return context_.scriptExtra[scriptIndex_];
2088 } // namespace frontend
2089 } // namespace js
2091 #endif // frontend_CompilationStencil_h