Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / js / src / frontend / Stencil.cpp
blob67ed2a90caeb054610ca2a41df25e1735bd4c36e
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 #include "frontend/Stencil.h"
9 #include "mozilla/AlreadyAddRefed.h" // already_AddRefed
10 #include "mozilla/Assertions.h" // MOZ_RELEASE_ASSERT
11 #include "mozilla/Maybe.h" // mozilla::Maybe
12 #include "mozilla/OperatorNewExtensions.h" // mozilla::KnownNotNull
13 #include "mozilla/PodOperations.h" // mozilla::PodCopy
14 #include "mozilla/RefPtr.h" // RefPtr
15 #include "mozilla/ScopeExit.h" // mozilla::ScopeExit
16 #include "mozilla/Sprintf.h" // SprintfLiteral
18 #include <algorithm> // std::fill
19 #include <string.h> // strlen
21 #include "ds/LifoAlloc.h" // LifoAlloc
22 #include "frontend/AbstractScopePtr.h" // ScopeIndex
23 #include "frontend/BytecodeCompiler.h" // CompileGlobalScriptToStencil, InstantiateStencils, CanLazilyParse, ParseModuleToStencil
24 #include "frontend/BytecodeSection.h" // EmitScriptThingsVector
25 #include "frontend/CompilationStencil.h" // CompilationStencil, CompilationState, ExtensibleCompilationStencil, CompilationGCOutput, CompilationStencilMerger
26 #include "frontend/FrontendContext.h"
27 #include "frontend/NameAnalysisTypes.h" // EnvironmentCoordinate
28 #include "frontend/ParserAtom.h" // ParserAtom, ParserAtomIndex, TaggedParserAtomIndex, ParserAtomsTable, Length{1,2,3}StaticParserString, InstantiateMarkedAtoms, InstantiateMarkedAtomsAsPermanent, GetWellKnownAtom
29 #include "frontend/ScopeBindingCache.h" // ScopeBindingCache
30 #include "frontend/SharedContext.h"
31 #include "frontend/StencilXdr.h" // XDRStencilEncoder, XDRStencilDecoder
32 #include "gc/AllocKind.h" // gc::AllocKind
33 #include "gc/Tracer.h" // TraceNullableRoot
34 #include "js/CallArgs.h" // JSNative
35 #include "js/CompileOptions.h" // JS::DecodeOptions, JS::ReadOnlyDecodeOptions
36 #include "js/experimental/JSStencil.h" // JS::Stencil
37 #include "js/GCAPI.h" // JS::AutoCheckCannotGC
38 #include "js/Printer.h" // js::Fprinter
39 #include "js/RealmOptions.h" // JS::RealmBehaviors
40 #include "js/RootingAPI.h" // Rooted
41 #include "js/Transcoding.h" // JS::TranscodeBuffer
42 #include "js/Utility.h" // js_malloc, js_calloc, js_free
43 #include "js/Value.h" // ObjectValue
44 #include "js/WasmModule.h" // JS::WasmModule
45 #include "vm/BigIntType.h" // ParseBigIntLiteral, BigIntLiteralIsZero
46 #include "vm/BindingKind.h" // BindingKind
47 #include "vm/EnvironmentObject.h"
48 #include "vm/GeneratorAndAsyncKind.h" // GeneratorKind, FunctionAsyncKind
49 #include "vm/JSContext.h" // JSContext
50 #include "vm/JSFunction.h" // JSFunction, GetFunctionPrototype, NewFunctionWithProto
51 #include "vm/JSObject.h" // JSObject, TenuredObject
52 #include "vm/JSONPrinter.h" // js::JSONPrinter
53 #include "vm/JSScript.h" // BaseScript, JSScript
54 #include "vm/Realm.h" // JS::Realm
55 #include "vm/RegExpObject.h" // js::RegExpObject
56 #include "vm/Scope.h" // Scope, *Scope, ScopeKind::*, ScopeKindString, ScopeIter, ScopeKindIsCatch, BindingIter, GetScopeDataTrailingNames, SizeOfParserScopeData
57 #include "vm/ScopeKind.h" // ScopeKind
58 #include "vm/SelfHosting.h" // SetClonedSelfHostedFunctionName
59 #include "vm/StaticStrings.h"
60 #include "vm/StencilEnums.h" // ImmutableScriptFlagsEnum
61 #include "vm/StringType.h" // JSAtom, js::CopyChars
62 #include "wasm/AsmJS.h" // InstantiateAsmJS
64 #include "vm/EnvironmentObject-inl.h" // JSObject::enclosingEnvironment
65 #include "vm/JSFunction-inl.h" // JSFunction::create
67 using namespace js;
68 using namespace js::frontend;
70 // These 2 functions are used to write the same code with lambda using auto
71 // arguments. The auto argument type is set by the Variant.match function of the
72 // InputScope variant. Thus dispatching to either a Scope* or to a
73 // ScopeStencilRef. This function can then be used as a way to specialize the
74 // code within the lambda without duplicating the code.
76 // Identically, an InputName is constructed using the scope type and the
77 // matching binding name type. This way, functions which are called by this
78 // lambda can manipulate an InputName and do not have to be duplicated.
80 // for (InputScopeIter si(...); si; si++) {
81 // si.scope().match([](auto& scope) {
82 // for (auto bi = InputBindingIter(scope); bi; bi++) {
83 // InputName name(scope, bi.name());
84 // }
85 // });
86 // }
87 static js::BindingIter InputBindingIter(Scope* ptr) {
88 return js::BindingIter(ptr);
91 static ParserBindingIter InputBindingIter(const ScopeStencilRef& ref) {
92 return ParserBindingIter(ref);
95 static ParserBindingIter InputBindingIter(const FakeStencilGlobalScope&) {
96 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No bindings on empty global.");
99 InputName InputScript::displayAtom() const {
100 return script_.match(
101 [](BaseScript* ptr) {
102 return InputName(ptr, ptr->function()->fullDisplayAtom());
104 [](const ScriptStencilRef& ref) {
105 return InputName(ref, ref.scriptData().functionAtom);
109 TaggedParserAtomIndex InputName::internInto(FrontendContext* fc,
110 ParserAtomsTable& parserAtoms,
111 CompilationAtomCache& atomCache) {
112 return variant_.match(
113 [&](JSAtom* ptr) -> TaggedParserAtomIndex {
114 return parserAtoms.internJSAtom(fc, atomCache, ptr);
116 [&](NameStencilRef& ref) -> TaggedParserAtomIndex {
117 return parserAtoms.internExternalParserAtomIndex(fc, ref.context_,
118 ref.atomIndex_);
122 bool InputName::isEqualTo(FrontendContext* fc, ParserAtomsTable& parserAtoms,
123 CompilationAtomCache& atomCache,
124 TaggedParserAtomIndex other,
125 JSAtom** otherCached) const {
126 return variant_.match(
127 [&](const JSAtom* ptr) -> bool {
128 if (ptr->hash() != parserAtoms.hash(other)) {
129 return false;
132 // JSAtom variant is used only on the main thread delazification,
133 // where JSContext is always available.
134 JSContext* cx = fc->maybeCurrentJSContext();
135 MOZ_ASSERT(cx);
137 if (!*otherCached) {
138 // TODO-Stencil:
139 // Here, we convert our name into a JSAtom*, and hard-crash on failure
140 // to allocate. This conversion should not be required as we should be
141 // able to iterate up snapshotted scope chains that use parser atoms.
143 // This will be fixed when the enclosing scopes are snapshotted.
145 // See bug 1690277.
146 AutoEnterOOMUnsafeRegion oomUnsafe;
147 *otherCached = parserAtoms.toJSAtom(cx, fc, other, atomCache);
148 if (!*otherCached) {
149 oomUnsafe.crash("InputName::isEqualTo");
151 } else {
152 MOZ_ASSERT(atomCache.getExistingAtomAt(cx, other) == *otherCached);
154 return ptr == *otherCached;
156 [&](const NameStencilRef& ref) -> bool {
157 return parserAtoms.isEqualToExternalParserAtomIndex(other, ref.context_,
158 ref.atomIndex_);
162 GenericAtom::GenericAtom(FrontendContext* fc, ParserAtomsTable& parserAtoms,
163 CompilationAtomCache& atomCache,
164 TaggedParserAtomIndex index)
165 : ref(EmitterName(fc, parserAtoms, atomCache, index)) {
166 hash = parserAtoms.hash(index);
169 GenericAtom::GenericAtom(const CompilationStencil& context,
170 TaggedParserAtomIndex index)
171 : ref(StencilName{context, index}) {
172 if (index.isParserAtomIndex()) {
173 ParserAtom* atom = context.parserAtomData[index.toParserAtomIndex()];
174 hash = atom->hash();
175 } else {
176 hash = index.staticOrWellKnownHash();
180 GenericAtom::GenericAtom(ScopeStencilRef& scope, TaggedParserAtomIndex index)
181 : GenericAtom(scope.context_, index) {}
183 BindingHasher<TaggedParserAtomIndex>::Lookup::Lookup(ScopeStencilRef& scope_ref,
184 const GenericAtom& other)
185 : keyStencil(scope_ref.context_), other(other) {}
187 bool GenericAtom::operator==(const GenericAtom& other) const {
188 return ref.match(
189 [&other](const EmitterName& name) -> bool {
190 return other.ref.match(
191 [&name](const EmitterName& other) -> bool {
192 // We never have multiple Emitter context at the same time.
193 MOZ_ASSERT(name.fc == other.fc);
194 MOZ_ASSERT(&name.parserAtoms == &other.parserAtoms);
195 MOZ_ASSERT(&name.atomCache == &other.atomCache);
196 return name.index == other.index;
198 [&name](const StencilName& other) -> bool {
199 return name.parserAtoms.isEqualToExternalParserAtomIndex(
200 name.index, other.stencil, other.index);
202 [&name](JSAtom* other) -> bool {
203 // JSAtom variant is used only on the main thread delazification,
204 // where JSContext is always available.
205 JSContext* cx = name.fc->maybeCurrentJSContext();
206 MOZ_ASSERT(cx);
207 AutoEnterOOMUnsafeRegion oomUnsafe;
208 JSAtom* namePtr = name.parserAtoms.toJSAtom(
209 cx, name.fc, name.index, name.atomCache);
210 if (!namePtr) {
211 oomUnsafe.crash("GenericAtom(EmitterName == JSAtom*)");
213 return namePtr == other;
216 [&other](const StencilName& name) -> bool {
217 return other.ref.match(
218 [&name](const EmitterName& other) -> bool {
219 return other.parserAtoms.isEqualToExternalParserAtomIndex(
220 other.index, name.stencil, name.index);
222 [&name](const StencilName& other) -> bool {
223 // Technically it is possible to have multiple stencils, but in
224 // this particular case let's assume we never encounter a case
225 // where we are comparing names from different stencils.
227 // The reason this assumption is safe today is that we are only
228 // using this in the context of a stencil-delazification, where
229 // the only StencilNames are coming from the CompilationStencil
230 // provided to CompilationInput::initFromStencil.
231 MOZ_ASSERT(&name.stencil == &other.stencil);
232 return name.index == other.index;
234 [](JSAtom* other) -> bool {
235 MOZ_CRASH("Never used.");
236 return false;
239 [&other](JSAtom* name) -> bool {
240 return other.ref.match(
241 [&name](const EmitterName& other) -> bool {
242 // JSAtom variant is used only on the main thread delazification,
243 // where JSContext is always available.
244 JSContext* cx = other.fc->maybeCurrentJSContext();
245 MOZ_ASSERT(cx);
246 AutoEnterOOMUnsafeRegion oomUnsafe;
247 JSAtom* otherPtr = other.parserAtoms.toJSAtom(
248 cx, other.fc, other.index, other.atomCache);
249 if (!otherPtr) {
250 oomUnsafe.crash("GenericAtom(JSAtom* == EmitterName)");
252 return name == otherPtr;
254 [](const StencilName& other) -> bool {
255 MOZ_CRASH("Never used.");
256 return false;
258 [&name](JSAtom* other) -> bool { return name == other; });
262 #ifdef DEBUG
263 template <typename SpanT, typename VecT>
264 void AssertBorrowingSpan(const SpanT& span, const VecT& vec) {
265 MOZ_ASSERT(span.size() == vec.length());
266 MOZ_ASSERT(span.data() == vec.begin());
268 #endif
270 bool ScopeBindingCache::canCacheFor(Scope* ptr) {
271 MOZ_CRASH("Unexpected scope chain type: Scope*");
274 bool ScopeBindingCache::canCacheFor(ScopeStencilRef ref) {
275 MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef");
278 bool ScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& ref) {
279 MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope");
282 BindingMap<JSAtom*>* ScopeBindingCache::createCacheFor(Scope* ptr) {
283 MOZ_CRASH("Unexpected scope chain type: Scope*");
286 BindingMap<JSAtom*>* ScopeBindingCache::lookupScope(Scope* ptr,
287 CacheGeneration gen) {
288 MOZ_CRASH("Unexpected scope chain type: Scope*");
291 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::createCacheFor(
292 ScopeStencilRef ref) {
293 MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef");
296 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::lookupScope(
297 ScopeStencilRef ref, CacheGeneration gen) {
298 MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef");
301 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::createCacheFor(
302 const FakeStencilGlobalScope& ref) {
303 MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope");
306 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::lookupScope(
307 const FakeStencilGlobalScope& ref, CacheGeneration gen) {
308 MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope");
311 bool NoScopeBindingCache::canCacheFor(Scope* ptr) { return false; }
313 bool NoScopeBindingCache::canCacheFor(ScopeStencilRef ref) { return false; }
315 bool NoScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& ref) {
316 return false;
319 bool RuntimeScopeBindingCache::canCacheFor(Scope* ptr) { return true; }
321 BindingMap<JSAtom*>* RuntimeScopeBindingCache::createCacheFor(Scope* ptr) {
322 BaseScopeData* dataPtr = ptr->rawData();
323 BindingMap<JSAtom*> bindingCache;
324 if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) {
325 return nullptr;
328 return lookupScope(ptr, cacheGeneration);
331 BindingMap<JSAtom*>* RuntimeScopeBindingCache::lookupScope(
332 Scope* ptr, CacheGeneration gen) {
333 MOZ_ASSERT(gen == cacheGeneration);
334 BaseScopeData* dataPtr = ptr->rawData();
335 auto valuePtr = scopeMap.lookup(dataPtr);
336 if (!valuePtr) {
337 return nullptr;
339 return &valuePtr->value();
342 bool StencilScopeBindingCache::canCacheFor(ScopeStencilRef ref) { return true; }
344 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::createCacheFor(
345 ScopeStencilRef ref) {
346 #ifdef DEBUG
347 AssertBorrowingSpan(ref.context_.scopeNames, merger_.getResult().scopeNames);
348 #endif
349 auto* dataPtr = ref.context_.scopeNames[ref.scopeIndex_];
350 BindingMap<TaggedParserAtomIndex> bindingCache;
351 if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) {
352 return nullptr;
355 return lookupScope(ref, 1);
358 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::lookupScope(
359 ScopeStencilRef ref, CacheGeneration gen) {
360 #ifdef DEBUG
361 AssertBorrowingSpan(ref.context_.scopeNames, merger_.getResult().scopeNames);
362 #endif
363 auto* dataPtr = ref.context_.scopeNames[ref.scopeIndex_];
364 auto ptr = scopeMap.lookup(dataPtr);
365 if (!ptr) {
366 return nullptr;
368 return &ptr->value();
371 static AbstractBaseScopeData<TaggedParserAtomIndex>
372 moduleGlobalAbstractScopeData;
374 bool StencilScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& ref) {
375 return true;
378 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::createCacheFor(
379 const FakeStencilGlobalScope& ref) {
380 auto* dataPtr = &moduleGlobalAbstractScopeData;
381 BindingMap<TaggedParserAtomIndex> bindingCache;
382 if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) {
383 return nullptr;
386 return lookupScope(ref, 1);
389 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::lookupScope(
390 const FakeStencilGlobalScope& ref, CacheGeneration gen) {
391 auto* dataPtr = &moduleGlobalAbstractScopeData;
392 auto ptr = scopeMap.lookup(dataPtr);
393 if (!ptr) {
394 return nullptr;
396 return &ptr->value();
399 bool ScopeContext::init(FrontendContext* fc, CompilationInput& input,
400 ParserAtomsTable& parserAtoms,
401 ScopeBindingCache* scopeCache, InheritThis inheritThis,
402 JSObject* enclosingEnv) {
403 // Record the scopeCache to be used while looking up NameLocation bindings.
404 this->scopeCache = scopeCache;
405 scopeCacheGen = scopeCache->getCurrentGeneration();
407 InputScope maybeNonDefaultEnclosingScope(
408 input.maybeNonDefaultEnclosingScope());
410 // If this eval is in response to Debugger.Frame.eval, we may have an
411 // incomplete scope chain. In order to provide a better debugging experience,
412 // we inspect the (optional) environment chain to determine it's enclosing
413 // FunctionScope if there is one. If there is no such scope, we use the
414 // orignal scope provided.
416 // NOTE: This is used to compute the ThisBinding kind and to allow access to
417 // private fields and methods, while other contextual information only
418 // uses the actual scope passed to the compile.
419 auto effectiveScope =
420 determineEffectiveScope(maybeNonDefaultEnclosingScope, enclosingEnv);
422 if (inheritThis == InheritThis::Yes) {
423 computeThisBinding(effectiveScope);
424 computeThisEnvironment(maybeNonDefaultEnclosingScope);
426 computeInScope(maybeNonDefaultEnclosingScope);
428 cacheEnclosingScope(input.enclosingScope);
430 if (input.target == CompilationInput::CompilationTarget::Eval) {
431 if (!cacheEnclosingScopeBindingForEval(fc, input, parserAtoms)) {
432 return false;
434 if (!cachePrivateFieldsForEval(fc, input, enclosingEnv, effectiveScope,
435 parserAtoms)) {
436 return false;
440 return true;
443 void ScopeContext::computeThisEnvironment(const InputScope& enclosingScope) {
444 uint32_t envCount = 0;
445 for (InputScopeIter si(enclosingScope); si; si++) {
446 if (si.kind() == ScopeKind::Function) {
447 // Arrow function inherit the "this" environment of the enclosing script,
448 // so continue ignore them.
449 if (!si.scope().isArrow()) {
450 allowNewTarget = true;
452 if (si.scope().allowSuperProperty()) {
453 allowSuperProperty = true;
454 enclosingThisEnvironmentHops = envCount;
457 if (si.scope().isClassConstructor()) {
458 memberInitializers =
459 si.scope().useMemberInitializers()
460 ? mozilla::Some(si.scope().getMemberInitializers())
461 : mozilla::Some(MemberInitializers::Empty());
462 MOZ_ASSERT(memberInitializers->valid);
463 } else {
464 if (si.scope().isSyntheticFunction()) {
465 allowArguments = false;
469 if (si.scope().isDerivedClassConstructor()) {
470 allowSuperCall = true;
473 // Found the effective "this" environment, so stop.
474 return;
478 if (si.scope().hasEnvironment()) {
479 envCount++;
484 void ScopeContext::computeThisBinding(const InputScope& scope) {
485 // Inspect the scope-chain.
486 for (InputScopeIter si(scope); si; si++) {
487 if (si.kind() == ScopeKind::Module) {
488 thisBinding = ThisBinding::Module;
489 return;
492 if (si.kind() == ScopeKind::Function) {
493 // Arrow functions don't have their own `this` binding.
494 if (si.scope().isArrow()) {
495 continue;
498 // Derived class constructors (and their nested arrow functions and evals)
499 // use ThisBinding::DerivedConstructor, which ensures TDZ checks happen
500 // when accessing |this|.
501 if (si.scope().isDerivedClassConstructor()) {
502 thisBinding = ThisBinding::DerivedConstructor;
503 } else {
504 thisBinding = ThisBinding::Function;
507 return;
511 thisBinding = ThisBinding::Global;
514 void ScopeContext::computeInScope(const InputScope& enclosingScope) {
515 for (InputScopeIter si(enclosingScope); si; si++) {
516 if (si.kind() == ScopeKind::ClassBody) {
517 inClass = true;
520 if (si.kind() == ScopeKind::With) {
521 inWith = true;
526 void ScopeContext::cacheEnclosingScope(const InputScope& enclosingScope) {
527 if (enclosingScope.isNull()) {
528 return;
531 enclosingScopeEnvironmentChainLength =
532 enclosingScope.environmentChainLength();
533 enclosingScopeKind = enclosingScope.kind();
535 if (enclosingScopeKind == ScopeKind::Function) {
536 enclosingScopeIsArrow = enclosingScope.isArrow();
539 enclosingScopeHasEnvironment = enclosingScope.hasEnvironment();
541 #ifdef DEBUG
542 hasNonSyntacticScopeOnChain =
543 enclosingScope.hasOnChain(ScopeKind::NonSyntactic);
545 // This computes a general answer for the query "does the enclosing scope
546 // have a function scope that needs a home object?", but it's only asserted
547 // if the parser parses eval body that contains `super` that needs a home
548 // object.
549 for (InputScopeIter si(enclosingScope); si; si++) {
550 if (si.kind() == ScopeKind::Function) {
551 if (si.scope().isArrow()) {
552 continue;
554 if (si.scope().allowSuperProperty() && si.scope().needsHomeObject()) {
555 hasFunctionNeedsHomeObjectOnChain = true;
557 break;
560 #endif
562 // Pre-fill the scope cache by iterating over all the names. Stop iterating
563 // as soon as we find a scope which already has a filled scope cache.
564 AutoEnterOOMUnsafeRegion oomUnsafe;
565 for (InputScopeIter si(enclosingScope); si; si++) {
566 // If the current scope already exists, then there is no need to go deeper
567 // as the scope which are encoded after this one should already be present
568 // in the cache.
569 bool hasScopeCache = si.scope().match([&](auto& scope_ref) -> bool {
570 MOZ_ASSERT(scopeCache->canCacheFor(scope_ref));
571 return scopeCache->lookupScope(scope_ref, scopeCacheGen);
573 if (hasScopeCache) {
574 return;
577 bool hasEnv = si.hasSyntacticEnvironment();
578 auto setCatchAll = [&](NameLocation loc) {
579 return si.scope().match([&](auto& scope_ref) {
580 using BindingMapPtr = decltype(scopeCache->createCacheFor(scope_ref));
581 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
582 if (!bindingMapPtr) {
583 oomUnsafe.crash(
584 "ScopeContext::cacheEnclosingScope: scopeCache->createCacheFor");
585 return;
588 bindingMapPtr->catchAll.emplace(loc);
591 auto createEmpty = [&]() {
592 return si.scope().match([&](auto& scope_ref) {
593 using BindingMapPtr = decltype(scopeCache->createCacheFor(scope_ref));
594 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
595 if (!bindingMapPtr) {
596 oomUnsafe.crash(
597 "ScopeContext::cacheEnclosingScope: scopeCache->createCacheFor");
598 return;
603 switch (si.kind()) {
604 case ScopeKind::Function:
605 if (hasEnv) {
606 if (si.scope().funHasExtensibleScope()) {
607 setCatchAll(NameLocation::Dynamic());
608 return;
611 si.scope().match([&](auto& scope_ref) {
612 using BindingMapPtr =
613 decltype(scopeCache->createCacheFor(scope_ref));
614 using Lookup =
615 typename std::remove_pointer_t<BindingMapPtr>::Lookup;
616 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
617 if (!bindingMapPtr) {
618 oomUnsafe.crash(
619 "ScopeContext::cacheEnclosingScope: "
620 "scopeCache->createCacheFor");
621 return;
624 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
625 NameLocation loc = bi.nameLocation();
626 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) {
627 continue;
629 auto ctxFreeKey = bi.name();
630 GenericAtom ctxKey(scope_ref, ctxFreeKey);
631 Lookup ctxLookup(scope_ref, ctxKey);
632 if (!bindingMapPtr->hashMap.put(ctxLookup, ctxFreeKey, loc)) {
633 oomUnsafe.crash(
634 "ScopeContext::cacheEnclosingScope: bindingMapPtr->put");
635 return;
639 } else {
640 createEmpty();
642 break;
644 case ScopeKind::StrictEval:
645 case ScopeKind::FunctionBodyVar:
646 case ScopeKind::Lexical:
647 case ScopeKind::NamedLambda:
648 case ScopeKind::StrictNamedLambda:
649 case ScopeKind::SimpleCatch:
650 case ScopeKind::Catch:
651 case ScopeKind::FunctionLexical:
652 case ScopeKind::ClassBody:
653 if (hasEnv) {
654 si.scope().match([&](auto& scope_ref) {
655 using BindingMapPtr =
656 decltype(scopeCache->createCacheFor(scope_ref));
657 using Lookup =
658 typename std::remove_pointer_t<BindingMapPtr>::Lookup;
659 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
660 if (!bindingMapPtr) {
661 oomUnsafe.crash(
662 "ScopeContext::cacheEnclosingScope: "
663 "scopeCache->createCacheFor");
664 return;
667 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
668 NameLocation loc = bi.nameLocation();
669 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) {
670 continue;
672 auto ctxFreeKey = bi.name();
673 GenericAtom ctxKey(scope_ref, ctxFreeKey);
674 Lookup ctxLookup(scope_ref, ctxKey);
675 if (!bindingMapPtr->hashMap.putNew(ctxLookup, ctxFreeKey, loc)) {
676 oomUnsafe.crash(
677 "ScopeContext::cacheEnclosingScope: bindingMapPtr->put");
678 return;
682 } else {
683 createEmpty();
685 break;
687 case ScopeKind::Module:
688 // This case is used only when delazifying a function inside
689 // module.
690 // Initial compilation of module doesn't have enlcosing scope.
691 if (hasEnv) {
692 si.scope().match([&](auto& scope_ref) {
693 using BindingMapPtr =
694 decltype(scopeCache->createCacheFor(scope_ref));
695 using Lookup =
696 typename std::remove_pointer_t<BindingMapPtr>::Lookup;
697 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
698 if (!bindingMapPtr) {
699 oomUnsafe.crash(
700 "ScopeContext::cacheEnclosingScope: "
701 "scopeCache->createCacheFor");
702 return;
705 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
706 // Imports are on the environment but are indirect
707 // bindings and must be accessed dynamically instead of
708 // using an EnvironmentCoordinate.
709 NameLocation loc = bi.nameLocation();
710 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate &&
711 loc.kind() != NameLocation::Kind::Import) {
712 continue;
714 auto ctxFreeKey = bi.name();
715 GenericAtom ctxKey(scope_ref, ctxFreeKey);
716 Lookup ctxLookup(scope_ref, ctxKey);
717 if (!bindingMapPtr->hashMap.putNew(ctxLookup, ctxFreeKey, loc)) {
718 oomUnsafe.crash(
719 "ScopeContext::cacheEnclosingScope: bindingMapPtr->put");
720 return;
724 } else {
725 createEmpty();
727 break;
729 case ScopeKind::Eval:
730 // As an optimization, if the eval doesn't have its own var
731 // environment and its immediate enclosing scope is a global
732 // scope, all accesses are global.
733 if (!hasEnv) {
734 ScopeKind kind = si.scope().enclosing().kind();
735 if (kind == ScopeKind::Global || kind == ScopeKind::NonSyntactic) {
736 setCatchAll(NameLocation::Global(BindingKind::Var));
737 return;
741 setCatchAll(NameLocation::Dynamic());
742 return;
744 case ScopeKind::Global:
745 setCatchAll(NameLocation::Global(BindingKind::Var));
746 return;
748 case ScopeKind::With:
749 case ScopeKind::NonSyntactic:
750 setCatchAll(NameLocation::Dynamic());
751 return;
753 case ScopeKind::WasmInstance:
754 case ScopeKind::WasmFunction:
755 MOZ_CRASH("No direct eval inside wasm functions");
759 MOZ_CRASH("Malformed scope chain");
762 // Given an input scope, possibly refine this to a more precise scope.
763 // This is used during eval in the debugger to provide the appropriate scope and
764 // ThisBinding kind and environment, which is key to making private field eval
765 // work correctly.
767 // The trick here is that an eval may have a non-syntatic scope but nevertheless
768 // have an 'interesting' environment which can be traversed to find the
769 // appropriate scope the the eval to function as desired. See the diagram below.
771 // Eval Scope Eval Env Frame Env Frame Scope
772 // ============ ============= ========= =============
774 // NonSyntactic
775 // |
776 // v
777 // null DebugEnvProxy LexicalScope
778 // | |
779 // v v
780 // DebugEnvProxy --> CallObj --> FunctionScope
781 // | | |
782 // v v v
783 // ... ... ...
785 InputScope ScopeContext::determineEffectiveScope(InputScope& scope,
786 JSObject* environment) {
787 MOZ_ASSERT(effectiveScopeHops == 0);
788 // If the scope-chain is non-syntactic, we may still determine a more precise
789 // effective-scope to use instead.
790 if (environment && scope.hasOnChain(ScopeKind::NonSyntactic)) {
791 JSObject* env = environment;
792 while (env) {
793 // Look at target of any DebugEnvironmentProxy, but be sure to use
794 // enclosingEnvironment() of the proxy itself.
795 JSObject* unwrapped = env;
796 if (env->is<DebugEnvironmentProxy>()) {
797 unwrapped = &env->as<DebugEnvironmentProxy>().environment();
798 #ifdef DEBUG
799 enclosingEnvironmentIsDebugProxy_ = true;
800 #endif
803 if (unwrapped->is<CallObject>()) {
804 JSFunction* callee = &unwrapped->as<CallObject>().callee();
805 return InputScope(callee->nonLazyScript()->bodyScope());
808 env = env->enclosingEnvironment();
809 effectiveScopeHops++;
813 return scope;
816 static uint32_t DepthOfNearestVarScopeForDirectEval(const InputScope& scope) {
817 uint32_t depth = 0;
818 if (scope.isNull()) {
819 return depth;
821 for (InputScopeIter si(scope); si; si++) {
822 depth++;
823 switch (si.scope().kind()) {
824 case ScopeKind::Function:
825 case ScopeKind::FunctionBodyVar:
826 case ScopeKind::Global:
827 case ScopeKind::NonSyntactic:
828 return depth;
829 default:
830 break;
833 return depth;
836 bool ScopeContext::cacheEnclosingScopeBindingForEval(
837 FrontendContext* fc, CompilationInput& input,
838 ParserAtomsTable& parserAtoms) {
839 enclosingLexicalBindingCache_.emplace();
841 uint32_t varScopeDepth =
842 DepthOfNearestVarScopeForDirectEval(input.enclosingScope);
843 uint32_t depth = 0;
844 for (InputScopeIter si(input.enclosingScope); si; si++) {
845 bool success = si.scope().match([&](auto& scope_ref) {
846 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
847 switch (bi.kind()) {
848 case BindingKind::Let: {
849 // Annex B.3.5 allows redeclaring simple (non-destructured)
850 // catch parameters with var declarations.
851 bool annexB35Allowance = si.kind() == ScopeKind::SimpleCatch;
852 if (!annexB35Allowance) {
853 auto kind = ScopeKindIsCatch(si.kind())
854 ? EnclosingLexicalBindingKind::CatchParameter
855 : EnclosingLexicalBindingKind::Let;
856 InputName binding(scope_ref, bi.name());
857 if (!addToEnclosingLexicalBindingCache(
858 fc, parserAtoms, input.atomCache, binding, kind)) {
859 return false;
862 break;
865 case BindingKind::Const: {
866 InputName binding(scope_ref, bi.name());
867 if (!addToEnclosingLexicalBindingCache(
868 fc, parserAtoms, input.atomCache, binding,
869 EnclosingLexicalBindingKind::Const)) {
870 return false;
872 break;
875 case BindingKind::Synthetic: {
876 InputName binding(scope_ref, bi.name());
877 if (!addToEnclosingLexicalBindingCache(
878 fc, parserAtoms, input.atomCache, binding,
879 EnclosingLexicalBindingKind::Synthetic)) {
880 return false;
882 break;
885 case BindingKind::PrivateMethod: {
886 InputName binding(scope_ref, bi.name());
887 if (!addToEnclosingLexicalBindingCache(
888 fc, parserAtoms, input.atomCache, binding,
889 EnclosingLexicalBindingKind::PrivateMethod)) {
890 return false;
892 break;
895 case BindingKind::Import:
896 case BindingKind::FormalParameter:
897 case BindingKind::Var:
898 case BindingKind::NamedLambdaCallee:
899 break;
902 return true;
904 if (!success) {
905 return false;
908 if (++depth == varScopeDepth) {
909 break;
913 return true;
916 bool ScopeContext::addToEnclosingLexicalBindingCache(
917 FrontendContext* fc, ParserAtomsTable& parserAtoms,
918 CompilationAtomCache& atomCache, InputName& name,
919 EnclosingLexicalBindingKind kind) {
920 TaggedParserAtomIndex parserName =
921 name.internInto(fc, parserAtoms, atomCache);
922 if (!parserName) {
923 return false;
926 // Same lexical binding can appear multiple times across scopes.
928 // enclosingLexicalBindingCache_ map is used for detecting conflicting
929 // `var` binding, and inner binding should be reported in the error.
931 // cacheEnclosingScopeBindingForEval iterates from inner scope, and
932 // inner-most binding is added to the map first.
934 // Do not overwrite the value with outer bindings.
935 auto p = enclosingLexicalBindingCache_->lookupForAdd(parserName);
936 if (!p) {
937 if (!enclosingLexicalBindingCache_->add(p, parserName, kind)) {
938 ReportOutOfMemory(fc);
939 return false;
943 return true;
946 static bool IsPrivateField(Scope*, JSAtom* atom) {
947 MOZ_ASSERT(atom->length() > 0);
949 JS::AutoCheckCannotGC nogc;
950 if (atom->hasLatin1Chars()) {
951 return atom->latin1Chars(nogc)[0] == '#';
954 return atom->twoByteChars(nogc)[0] == '#';
957 static bool IsPrivateField(ScopeStencilRef& scope, TaggedParserAtomIndex atom) {
958 if (atom.isParserAtomIndex()) {
959 const CompilationStencil& context = scope.context_;
960 ParserAtom* parserAtom = context.parserAtomData[atom.toParserAtomIndex()];
961 return parserAtom->isPrivateName();
964 #ifdef DEBUG
965 if (atom.isWellKnownAtomId()) {
966 const auto& info = GetWellKnownAtomInfo(atom.toWellKnownAtomId());
967 // #constructor is a well-known term, but it is invalid private name.
968 MOZ_ASSERT(!(info.length > 1 && info.content[0] == '#'));
969 } else if (atom.isLength2StaticParserString()) {
970 char content[2];
971 ParserAtomsTable::getLength2Content(atom.toLength2StaticParserString(),
972 content);
973 // # character is not part of the allowed character of static strings.
974 MOZ_ASSERT(content[0] != '#');
976 #endif
978 return false;
981 static bool IsPrivateField(const FakeStencilGlobalScope&,
982 TaggedParserAtomIndex) {
983 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No private fields on empty global.");
986 bool ScopeContext::cachePrivateFieldsForEval(FrontendContext* fc,
987 CompilationInput& input,
988 JSObject* enclosingEnvironment,
989 const InputScope& effectiveScope,
990 ParserAtomsTable& parserAtoms) {
991 effectiveScopePrivateFieldCache_.emplace();
993 // We compute an environment coordinate relative to the effective scope
994 // environment. In order to safely consume these environment coordinates,
995 // we re-map them to include the hops to get the to the effective scope:
996 // see EmitterScope::lookupPrivate
997 uint32_t hops = effectiveScopeHops;
998 for (InputScopeIter si(effectiveScope); si; si++) {
999 if (si.scope().kind() == ScopeKind::ClassBody) {
1000 uint32_t slots = 0;
1001 bool success = si.scope().match([&](auto& scope_ref) {
1002 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
1003 if (bi.kind() == BindingKind::PrivateMethod ||
1004 (bi.kind() == BindingKind::Synthetic &&
1005 IsPrivateField(scope_ref, bi.name()))) {
1006 InputName binding(scope_ref, bi.name());
1007 auto parserName =
1008 binding.internInto(fc, parserAtoms, input.atomCache);
1009 if (!parserName) {
1010 return false;
1013 NameLocation loc = NameLocation::DebugEnvironmentCoordinate(
1014 bi.kind(), hops, slots);
1016 if (!effectiveScopePrivateFieldCache_->put(parserName, loc)) {
1017 ReportOutOfMemory(fc);
1018 return false;
1021 slots++;
1023 return true;
1025 if (!success) {
1026 return false;
1030 // Hops is only consumed by GetAliasedDebugVar, which uses this to
1031 // traverse the debug environment chain. See the [SMDOC] for Debug
1032 // Environment Chain, which explains why we don't check for
1033 // isEnvironment when computing hops here (basically, debug proxies
1034 // pretend all scopes have environments, even if they were actually
1035 // optimized out).
1036 hops++;
1039 return true;
1042 #ifdef DEBUG
1043 static bool NameIsOnEnvironment(FrontendContext* fc,
1044 ParserAtomsTable& parserAtoms,
1045 CompilationAtomCache& atomCache,
1046 InputScope& scope, TaggedParserAtomIndex name) {
1047 JSAtom* jsname = nullptr;
1048 return scope.match([&](auto& scope_ref) {
1049 if (std::is_same_v<decltype(scope_ref), FakeStencilGlobalScope&>) {
1050 // This condition is added to handle the FakeStencilGlobalScope which is
1051 // used to emulate the global object when delazifying while executing, and
1052 // which is not provided by the Stencil.
1053 return true;
1055 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
1056 // If found, the name must already be on the environment or an import,
1057 // or else there is a bug in the closed-over name analysis in the
1058 // Parser.
1059 InputName binding(scope_ref, bi.name());
1060 if (binding.isEqualTo(fc, parserAtoms, atomCache, name, &jsname)) {
1061 BindingLocation::Kind kind = bi.location().kind();
1063 if (bi.hasArgumentSlot()) {
1064 // The following is equivalent to
1065 // functionScope.script()->functionAllowsParameterRedeclaration()
1066 if (scope.hasMappedArgsObj()) {
1067 // Check for duplicate positional formal parameters.
1068 using InputBindingIter = decltype(bi);
1069 for (InputBindingIter bi2(bi); bi2 && bi2.hasArgumentSlot();
1070 bi2++) {
1071 InputName binding2(scope_ref, bi2.name());
1072 if (binding2.isEqualTo(fc, parserAtoms, atomCache, name,
1073 &jsname)) {
1074 kind = bi2.location().kind();
1080 return kind == BindingLocation::Kind::Global ||
1081 kind == BindingLocation::Kind::Environment ||
1082 kind == BindingLocation::Kind::Import;
1086 // If not found, assume it's on the global or dynamically accessed.
1087 return true;
1090 #endif
1092 NameLocation ScopeContext::searchInEnclosingScope(FrontendContext* fc,
1093 CompilationInput& input,
1094 ParserAtomsTable& parserAtoms,
1095 TaggedParserAtomIndex name) {
1096 MOZ_ASSERT(input.target ==
1097 CompilationInput::CompilationTarget::Delazification ||
1098 input.target == CompilationInput::CompilationTarget::Eval);
1100 MOZ_ASSERT(scopeCache);
1101 if (scopeCacheGen != scopeCache->getCurrentGeneration()) {
1102 return searchInEnclosingScopeNoCache(fc, input, parserAtoms, name);
1105 #ifdef DEBUG
1106 // Catch assertion failures in the NoCache variant before looking at the
1107 // cached content.
1108 NameLocation expect =
1109 searchInEnclosingScopeNoCache(fc, input, parserAtoms, name);
1110 #endif
1112 NameLocation found =
1113 searchInEnclosingScopeWithCache(fc, input, parserAtoms, name);
1114 MOZ_ASSERT(expect == found);
1115 return found;
1118 NameLocation ScopeContext::searchInEnclosingScopeWithCache(
1119 FrontendContext* fc, CompilationInput& input, ParserAtomsTable& parserAtoms,
1120 TaggedParserAtomIndex name) {
1121 MOZ_ASSERT(input.target ==
1122 CompilationInput::CompilationTarget::Delazification ||
1123 input.target == CompilationInput::CompilationTarget::Eval);
1125 // Generic atom of the looked up name.
1126 GenericAtom genName(fc, parserAtoms, input.atomCache, name);
1127 mozilla::Maybe<NameLocation> found;
1129 // Number of enclosing scope we walked over.
1130 uint8_t hops = 0;
1132 for (InputScopeIter si(input.enclosingScope); si; si++) {
1133 MOZ_ASSERT(NameIsOnEnvironment(fc, parserAtoms, input.atomCache, si.scope(),
1134 name));
1136 // If the result happens to be in the cached content of the scope that we
1137 // are iterating over, then return it.
1138 si.scope().match([&](auto& scope_ref) {
1139 using BindingMapPtr =
1140 decltype(scopeCache->lookupScope(scope_ref, scopeCacheGen));
1141 BindingMapPtr bindingMapPtr =
1142 scopeCache->lookupScope(scope_ref, scopeCacheGen);
1143 MOZ_ASSERT(bindingMapPtr);
1145 auto& bindingMap = *bindingMapPtr;
1146 if (bindingMap.catchAll.isSome()) {
1147 found = bindingMap.catchAll;
1148 return;
1151 // The scope_ref is given as argument to know where to lookup the key
1152 // index of the hash table if the names have to be compared.
1153 using Lookup = typename std::remove_pointer_t<BindingMapPtr>::Lookup;
1154 Lookup ctxName(scope_ref, genName);
1155 auto ptr = bindingMap.hashMap.lookup(ctxName);
1156 if (!ptr) {
1157 return;
1160 found.emplace(ptr->value());
1163 if (found.isSome()) {
1164 // Cached entries do not store the number of hops, as it might be reused
1165 // by multiple inner functions, which might different number of hops.
1166 found = found.map([&hops](NameLocation loc) {
1167 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) {
1168 return loc;
1170 return loc.addHops(hops);
1172 return found.value();
1175 bool hasEnv = si.hasSyntacticEnvironment();
1177 if (hasEnv) {
1178 MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1);
1179 hops++;
1183 MOZ_CRASH("Malformed scope chain");
1186 NameLocation ScopeContext::searchInEnclosingScopeNoCache(
1187 FrontendContext* fc, CompilationInput& input, ParserAtomsTable& parserAtoms,
1188 TaggedParserAtomIndex name) {
1189 MOZ_ASSERT(input.target ==
1190 CompilationInput::CompilationTarget::Delazification ||
1191 input.target == CompilationInput::CompilationTarget::Eval);
1193 // Cached JSAtom equivalent of the TaggedParserAtomIndex `name` argument.
1194 JSAtom* jsname = nullptr;
1196 // NameLocation which contains relative locations to access `name`.
1197 mozilla::Maybe<NameLocation> result;
1199 // Number of enclosing scoep we walked over.
1200 uint8_t hops = 0;
1202 for (InputScopeIter si(input.enclosingScope); si; si++) {
1203 MOZ_ASSERT(NameIsOnEnvironment(fc, parserAtoms, input.atomCache, si.scope(),
1204 name));
1206 bool hasEnv = si.hasSyntacticEnvironment();
1207 switch (si.kind()) {
1208 case ScopeKind::Function:
1209 if (hasEnv) {
1210 if (si.scope().funHasExtensibleScope()) {
1211 return NameLocation::Dynamic();
1214 si.scope().match([&](auto& scope_ref) {
1215 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
1216 InputName binding(scope_ref, bi.name());
1217 if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name,
1218 &jsname)) {
1219 continue;
1222 BindingLocation bindLoc = bi.location();
1223 // hasMappedArgsObj == script.functionAllowsParameterRedeclaration
1224 if (bi.hasArgumentSlot() && si.scope().hasMappedArgsObj()) {
1225 // Check for duplicate positional formal parameters.
1226 using InputBindingIter = decltype(bi);
1227 for (InputBindingIter bi2(bi); bi2 && bi2.hasArgumentSlot();
1228 bi2++) {
1229 if (bi.name() == bi2.name()) {
1230 bindLoc = bi2.location();
1235 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
1236 result.emplace(NameLocation::EnvironmentCoordinate(
1237 bi.kind(), hops, bindLoc.slot()));
1238 return;
1242 break;
1244 case ScopeKind::StrictEval:
1245 case ScopeKind::FunctionBodyVar:
1246 case ScopeKind::Lexical:
1247 case ScopeKind::NamedLambda:
1248 case ScopeKind::StrictNamedLambda:
1249 case ScopeKind::SimpleCatch:
1250 case ScopeKind::Catch:
1251 case ScopeKind::FunctionLexical:
1252 case ScopeKind::ClassBody:
1253 if (hasEnv) {
1254 si.scope().match([&](auto& scope_ref) {
1255 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
1256 InputName binding(scope_ref, bi.name());
1257 if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name,
1258 &jsname)) {
1259 continue;
1262 // The name must already have been marked as closed
1263 // over. If this assertion is hit, there is a bug in the
1264 // name analysis.
1265 BindingLocation bindLoc = bi.location();
1266 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
1267 result.emplace(NameLocation::EnvironmentCoordinate(
1268 bi.kind(), hops, bindLoc.slot()));
1269 return;
1273 break;
1275 case ScopeKind::Module:
1276 // This case is used only when delazifying a function inside
1277 // module.
1278 // Initial compilation of module doesn't have enlcosing scope.
1279 if (hasEnv) {
1280 si.scope().match([&](auto& scope_ref) {
1281 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
1282 InputName binding(scope_ref, bi.name());
1283 if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name,
1284 &jsname)) {
1285 continue;
1288 BindingLocation bindLoc = bi.location();
1290 // Imports are on the environment but are indirect
1291 // bindings and must be accessed dynamically instead of
1292 // using an EnvironmentCoordinate.
1293 if (bindLoc.kind() == BindingLocation::Kind::Import) {
1294 MOZ_ASSERT(si.kind() == ScopeKind::Module);
1295 result.emplace(NameLocation::Import());
1296 return;
1299 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
1300 result.emplace(NameLocation::EnvironmentCoordinate(
1301 bi.kind(), hops, bindLoc.slot()));
1302 return;
1306 break;
1308 case ScopeKind::Eval:
1309 // As an optimization, if the eval doesn't have its own var
1310 // environment and its immediate enclosing scope is a global
1311 // scope, all accesses are global.
1312 if (!hasEnv) {
1313 ScopeKind kind = si.scope().enclosing().kind();
1314 if (kind == ScopeKind::Global || kind == ScopeKind::NonSyntactic) {
1315 return NameLocation::Global(BindingKind::Var);
1318 return NameLocation::Dynamic();
1320 case ScopeKind::Global:
1321 return NameLocation::Global(BindingKind::Var);
1323 case ScopeKind::With:
1324 case ScopeKind::NonSyntactic:
1325 return NameLocation::Dynamic();
1327 case ScopeKind::WasmInstance:
1328 case ScopeKind::WasmFunction:
1329 MOZ_CRASH("No direct eval inside wasm functions");
1332 if (result.isSome()) {
1333 return result.value();
1336 if (hasEnv) {
1337 MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1);
1338 hops++;
1342 MOZ_CRASH("Malformed scope chain");
1345 mozilla::Maybe<ScopeContext::EnclosingLexicalBindingKind>
1346 ScopeContext::lookupLexicalBindingInEnclosingScope(TaggedParserAtomIndex name) {
1347 auto p = enclosingLexicalBindingCache_->lookup(name);
1348 if (!p) {
1349 return mozilla::Nothing();
1352 return mozilla::Some(p->value());
1355 bool ScopeContext::effectiveScopePrivateFieldCacheHas(
1356 TaggedParserAtomIndex name) {
1357 return effectiveScopePrivateFieldCache_->has(name);
1360 mozilla::Maybe<NameLocation> ScopeContext::getPrivateFieldLocation(
1361 TaggedParserAtomIndex name) {
1362 // The locations returned by this method are only valid for
1363 // traversing debug environments.
1365 // See the comment in cachePrivateFieldsForEval
1366 MOZ_ASSERT(enclosingEnvironmentIsDebugProxy_);
1367 auto p = effectiveScopePrivateFieldCache_->lookup(name);
1368 if (!p) {
1369 return mozilla::Nothing();
1371 return mozilla::Some(p->value());
1374 bool CompilationInput::initScriptSource(FrontendContext* fc) {
1375 source = do_AddRef(fc->getAllocator()->new_<ScriptSource>());
1376 if (!source) {
1377 return false;
1380 return source->initFromOptions(fc, options);
1383 bool CompilationInput::initForStandaloneFunctionInNonSyntacticScope(
1384 FrontendContext* fc, Handle<Scope*> functionEnclosingScope) {
1385 MOZ_ASSERT(!functionEnclosingScope->as<GlobalScope>().isSyntactic());
1387 target = CompilationTarget::StandaloneFunctionInNonSyntacticScope;
1388 if (!initScriptSource(fc)) {
1389 return false;
1391 enclosingScope = InputScope(functionEnclosingScope);
1392 return true;
1395 FunctionSyntaxKind CompilationInput::functionSyntaxKind() const {
1396 if (functionFlags().isClassConstructor()) {
1397 if (functionFlags().hasBaseScript() && isDerivedClassConstructor()) {
1398 return FunctionSyntaxKind::DerivedClassConstructor;
1400 return FunctionSyntaxKind::ClassConstructor;
1402 if (functionFlags().isMethod()) {
1403 if (functionFlags().hasBaseScript() && isSyntheticFunction()) {
1404 // return FunctionSyntaxKind::FieldInitializer;
1405 MOZ_ASSERT_UNREACHABLE(
1406 "Lazy parsing of class field initializers not supported (yet)");
1408 return FunctionSyntaxKind::Method;
1410 if (functionFlags().isGetter()) {
1411 return FunctionSyntaxKind::Getter;
1413 if (functionFlags().isSetter()) {
1414 return FunctionSyntaxKind::Setter;
1416 if (functionFlags().isArrow()) {
1417 return FunctionSyntaxKind::Arrow;
1419 return FunctionSyntaxKind::Statement;
1422 bool CompilationInput::internExtraBindings(FrontendContext* fc,
1423 ParserAtomsTable& parserAtoms) {
1424 MOZ_ASSERT(hasExtraBindings());
1426 for (auto& bindingInfo : *maybeExtraBindings_) {
1427 if (bindingInfo.isShadowed) {
1428 continue;
1431 const char* chars = bindingInfo.nameChars.get();
1432 auto index = parserAtoms.internUtf8(
1433 fc, reinterpret_cast<const mozilla::Utf8Unit*>(chars), strlen(chars));
1434 if (!index) {
1435 return false;
1438 bindingInfo.nameIndex = index;
1441 return true;
1444 void InputScope::trace(JSTracer* trc) {
1445 using ScopePtr = Scope*;
1446 if (scope_.is<ScopePtr>()) {
1447 ScopePtr* ptrAddr = &scope_.as<ScopePtr>();
1448 TraceNullableRoot(trc, ptrAddr, "compilation-input-scope");
1452 void InputScript::trace(JSTracer* trc) {
1453 using ScriptPtr = BaseScript*;
1454 if (script_.is<ScriptPtr>()) {
1455 ScriptPtr* ptrAddr = &script_.as<ScriptPtr>();
1456 TraceNullableRoot(trc, ptrAddr, "compilation-input-lazy");
1460 void CompilationInput::trace(JSTracer* trc) {
1461 atomCache.trace(trc);
1462 lazy_.trace(trc);
1463 enclosingScope.trace(trc);
1466 bool CompilationSyntaxParseCache::init(FrontendContext* fc, LifoAlloc& alloc,
1467 ParserAtomsTable& parseAtoms,
1468 CompilationAtomCache& atomCache,
1469 const InputScript& lazy) {
1470 if (!copyFunctionInfo(fc, parseAtoms, atomCache, lazy)) {
1471 return false;
1473 bool success = lazy.raw().match([&](auto& ref) {
1474 if (!copyScriptInfo(fc, alloc, parseAtoms, atomCache, ref)) {
1475 return false;
1477 if (!copyClosedOverBindings(fc, alloc, parseAtoms, atomCache, ref)) {
1478 return false;
1480 return true;
1482 if (!success) {
1483 return false;
1485 #ifdef DEBUG
1486 isInitialized = true;
1487 #endif
1488 return true;
1491 bool CompilationSyntaxParseCache::copyFunctionInfo(
1492 FrontendContext* fc, ParserAtomsTable& parseAtoms,
1493 CompilationAtomCache& atomCache, const InputScript& lazy) {
1494 InputName name = lazy.displayAtom();
1495 if (!name.isNull()) {
1496 displayAtom_ = name.internInto(fc, parseAtoms, atomCache);
1497 if (!displayAtom_) {
1498 return false;
1502 funExtra_.immutableFlags = lazy.immutableFlags();
1503 funExtra_.extent = lazy.extent();
1504 if (funExtra_.useMemberInitializers()) {
1505 funExtra_.setMemberInitializers(lazy.getMemberInitializers());
1508 return true;
1511 bool CompilationSyntaxParseCache::copyScriptInfo(
1512 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
1513 CompilationAtomCache& atomCache, BaseScript* lazy) {
1514 using GCThingsSpan = mozilla::Span<TaggedScriptThingIndex>;
1515 using ScriptDataSpan = mozilla::Span<ScriptStencil>;
1516 using ScriptExtraSpan = mozilla::Span<ScriptStencilExtra>;
1517 cachedGCThings_ = GCThingsSpan(nullptr);
1518 cachedScriptData_ = ScriptDataSpan(nullptr);
1519 cachedScriptExtra_ = ScriptExtraSpan(nullptr);
1521 auto gcthings = lazy->gcthings();
1522 size_t length = gcthings.Length();
1523 if (length == 0) {
1524 return true;
1527 // Reduce the length to the first element which is not a function.
1528 for (size_t i = 0; i < length; i++) {
1529 gc::Cell* cell = gcthings[i].asCell();
1530 if (!cell || !cell->is<JSObject>()) {
1531 length = i;
1532 break;
1534 MOZ_ASSERT(cell->as<JSObject>()->is<JSFunction>());
1537 TaggedScriptThingIndex* gcThingsData =
1538 alloc.newArrayUninitialized<TaggedScriptThingIndex>(length);
1539 ScriptStencil* scriptData =
1540 alloc.newArrayUninitialized<ScriptStencil>(length);
1541 ScriptStencilExtra* scriptExtra =
1542 alloc.newArrayUninitialized<ScriptStencilExtra>(length);
1543 if (!gcThingsData || !scriptData || !scriptExtra) {
1544 ReportOutOfMemory(fc);
1545 return false;
1548 for (size_t i = 0; i < length; i++) {
1549 gc::Cell* cell = gcthings[i].asCell();
1550 JSFunction* fun = &cell->as<JSObject>()->as<JSFunction>();
1551 gcThingsData[i] = TaggedScriptThingIndex(ScriptIndex(i));
1552 new (mozilla::KnownNotNull, &scriptData[i]) ScriptStencil();
1553 ScriptStencil& data = scriptData[i];
1554 new (mozilla::KnownNotNull, &scriptExtra[i]) ScriptStencilExtra();
1555 ScriptStencilExtra& extra = scriptExtra[i];
1557 if (fun->fullDisplayAtom()) {
1558 TaggedParserAtomIndex displayAtom =
1559 parseAtoms.internJSAtom(fc, atomCache, fun->fullDisplayAtom());
1560 if (!displayAtom) {
1561 return false;
1563 data.functionAtom = displayAtom;
1565 data.functionFlags = fun->flags();
1567 BaseScript* lazy = fun->baseScript();
1568 extra.immutableFlags = lazy->immutableFlags();
1569 extra.extent = lazy->extent();
1571 // Info derived from parent compilation should not be set yet for our inner
1572 // lazy functions. Instead that info will be updated when we finish our
1573 // compilation.
1574 MOZ_ASSERT(lazy->hasEnclosingScript());
1577 cachedGCThings_ = GCThingsSpan(gcThingsData, length);
1578 cachedScriptData_ = ScriptDataSpan(scriptData, length);
1579 cachedScriptExtra_ = ScriptExtraSpan(scriptExtra, length);
1580 return true;
1583 bool CompilationSyntaxParseCache::copyScriptInfo(
1584 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
1585 CompilationAtomCache& atomCache, const ScriptStencilRef& lazy) {
1586 using GCThingsSpan = mozilla::Span<TaggedScriptThingIndex>;
1587 using ScriptDataSpan = mozilla::Span<ScriptStencil>;
1588 using ScriptExtraSpan = mozilla::Span<ScriptStencilExtra>;
1589 cachedGCThings_ = GCThingsSpan(nullptr);
1590 cachedScriptData_ = ScriptDataSpan(nullptr);
1591 cachedScriptExtra_ = ScriptExtraSpan(nullptr);
1593 size_t offset = lazy.scriptData().gcThingsOffset.index;
1594 size_t length = lazy.scriptData().gcThingsLength;
1595 if (length == 0) {
1596 return true;
1599 // Reduce the length to the first element which is not a function.
1600 for (size_t i = offset; i < offset + length; i++) {
1601 if (!lazy.context_.gcThingData[i].isFunction()) {
1602 length = i - offset;
1603 break;
1607 TaggedScriptThingIndex* gcThingsData =
1608 alloc.newArrayUninitialized<TaggedScriptThingIndex>(length);
1609 ScriptStencil* scriptData =
1610 alloc.newArrayUninitialized<ScriptStencil>(length);
1611 ScriptStencilExtra* scriptExtra =
1612 alloc.newArrayUninitialized<ScriptStencilExtra>(length);
1613 if (!gcThingsData || !scriptData || !scriptExtra) {
1614 ReportOutOfMemory(fc);
1615 return false;
1618 for (size_t i = 0; i < length; i++) {
1619 ScriptStencilRef inner{lazy.context_,
1620 lazy.context_.gcThingData[i + offset].toFunction()};
1621 gcThingsData[i] = TaggedScriptThingIndex(ScriptIndex(i));
1622 new (mozilla::KnownNotNull, &scriptData[i]) ScriptStencil();
1623 ScriptStencil& data = scriptData[i];
1624 ScriptStencilExtra& extra = scriptExtra[i];
1626 InputName name{inner, inner.scriptData().functionAtom};
1627 if (!name.isNull()) {
1628 auto displayAtom = name.internInto(fc, parseAtoms, atomCache);
1629 if (!displayAtom) {
1630 return false;
1632 data.functionAtom = displayAtom;
1634 data.functionFlags = inner.scriptData().functionFlags;
1636 extra = inner.scriptExtra();
1639 cachedGCThings_ = GCThingsSpan(gcThingsData, length);
1640 cachedScriptData_ = ScriptDataSpan(scriptData, length);
1641 cachedScriptExtra_ = ScriptExtraSpan(scriptExtra, length);
1642 return true;
1645 bool CompilationSyntaxParseCache::copyClosedOverBindings(
1646 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
1647 CompilationAtomCache& atomCache, BaseScript* lazy) {
1648 using ClosedOverBindingsSpan = mozilla::Span<TaggedParserAtomIndex>;
1649 closedOverBindings_ = ClosedOverBindingsSpan(nullptr);
1651 // The gcthings() array contains the inner function list followed by the
1652 // closed-over bindings data. Skip the inner function list, as it is already
1653 // cached in cachedGCThings_. See also: BaseScript::CreateLazy.
1654 size_t start = cachedGCThings_.Length();
1655 auto gcthings = lazy->gcthings();
1656 size_t length = gcthings.Length();
1657 MOZ_ASSERT(start <= length);
1658 if (length - start == 0) {
1659 return true;
1662 TaggedParserAtomIndex* closedOverBindings =
1663 alloc.newArrayUninitialized<TaggedParserAtomIndex>(length - start);
1664 if (!closedOverBindings) {
1665 ReportOutOfMemory(fc);
1666 return false;
1669 for (size_t i = start; i < length; i++) {
1670 gc::Cell* cell = gcthings[i].asCell();
1671 if (!cell) {
1672 closedOverBindings[i - start] = TaggedParserAtomIndex::null();
1673 continue;
1676 MOZ_ASSERT(cell->as<JSString>()->isAtom());
1678 auto name = static_cast<JSAtom*>(cell);
1679 auto parserAtom = parseAtoms.internJSAtom(fc, atomCache, name);
1680 if (!parserAtom) {
1681 return false;
1684 closedOverBindings[i - start] = parserAtom;
1687 closedOverBindings_ =
1688 ClosedOverBindingsSpan(closedOverBindings, length - start);
1689 return true;
1692 bool CompilationSyntaxParseCache::copyClosedOverBindings(
1693 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
1694 CompilationAtomCache& atomCache, const ScriptStencilRef& lazy) {
1695 using ClosedOverBindingsSpan = mozilla::Span<TaggedParserAtomIndex>;
1696 closedOverBindings_ = ClosedOverBindingsSpan(nullptr);
1698 // The gcthings array contains the inner function list followed by the
1699 // closed-over bindings data. Skip the inner function list, as it is already
1700 // cached in cachedGCThings_. See also: BaseScript::CreateLazy.
1701 size_t offset = lazy.scriptData().gcThingsOffset.index;
1702 size_t length = lazy.scriptData().gcThingsLength;
1703 size_t start = cachedGCThings_.Length();
1704 MOZ_ASSERT(start <= length);
1705 if (length - start == 0) {
1706 return true;
1708 length -= start;
1709 start += offset;
1711 // Atoms from the lazy.context (CompilationStencil) are not registered in the
1712 // the parseAtoms table. Thus we create a new span which will contain all the
1713 // interned atoms.
1714 TaggedParserAtomIndex* closedOverBindings =
1715 alloc.newArrayUninitialized<TaggedParserAtomIndex>(length);
1716 if (!closedOverBindings) {
1717 ReportOutOfMemory(fc);
1718 return false;
1721 for (size_t i = 0; i < length; i++) {
1722 auto gcThing = lazy.context_.gcThingData[i + start];
1723 if (gcThing.isNull()) {
1724 closedOverBindings[i] = TaggedParserAtomIndex::null();
1725 continue;
1728 MOZ_ASSERT(gcThing.isAtom());
1729 InputName name(lazy, gcThing.toAtom());
1730 auto parserAtom = name.internInto(fc, parseAtoms, atomCache);
1731 if (!parserAtom) {
1732 return false;
1735 closedOverBindings[i] = parserAtom;
1738 closedOverBindings_ = ClosedOverBindingsSpan(closedOverBindings, length);
1739 return true;
1742 template <typename T>
1743 PreAllocateableGCArray<T>::~PreAllocateableGCArray() {
1744 if (elems_) {
1745 js_free(elems_);
1746 elems_ = nullptr;
1750 template <typename T>
1751 bool PreAllocateableGCArray<T>::allocate(size_t length) {
1752 MOZ_ASSERT(empty());
1754 length_ = length;
1756 if (isInline()) {
1757 inlineElem_ = nullptr;
1758 return true;
1761 elems_ = reinterpret_cast<T*>(js_calloc(sizeof(T) * length_));
1762 if (!elems_) {
1763 return false;
1766 return true;
1769 template <typename T>
1770 bool PreAllocateableGCArray<T>::allocateWith(T init, size_t length) {
1771 MOZ_ASSERT(empty());
1773 length_ = length;
1775 if (isInline()) {
1776 inlineElem_ = init;
1777 return true;
1780 elems_ = reinterpret_cast<T*>(js_malloc(sizeof(T) * length_));
1781 if (!elems_) {
1782 return false;
1785 std::fill(elems_, elems_ + length_, init);
1786 return true;
1789 template <typename T>
1790 void PreAllocateableGCArray<T>::steal(Preallocated&& buffer) {
1791 MOZ_ASSERT(empty());
1793 length_ = buffer.length_;
1794 buffer.length_ = 0;
1796 if (isInline()) {
1797 inlineElem_ = nullptr;
1798 return;
1801 elems_ = reinterpret_cast<T*>(buffer.elems_);
1802 buffer.elems_ = nullptr;
1804 #ifdef DEBUG
1805 for (size_t i = 0; i < length_; i++) {
1806 MOZ_ASSERT(elems_[i] == nullptr);
1808 #endif
1811 template <typename T>
1812 void PreAllocateableGCArray<T>::trace(JSTracer* trc) {
1813 if (empty()) {
1814 return;
1817 if (isInline()) {
1818 TraceNullableRoot(trc, &inlineElem_, "PreAllocateableGCArray::inlineElem_");
1819 return;
1822 for (size_t i = 0; i < length_; i++) {
1823 TraceNullableRoot(trc, &elems_[i], "PreAllocateableGCArray::elems_");
1827 template <typename T>
1828 PreAllocateableGCArray<T>::Preallocated::~Preallocated() {
1829 if (elems_) {
1830 js_free(elems_);
1831 elems_ = nullptr;
1835 template <typename T>
1836 bool PreAllocateableGCArray<T>::Preallocated::allocate(size_t length) {
1837 MOZ_ASSERT(empty());
1839 length_ = length;
1841 if (isInline()) {
1842 return true;
1845 elems_ = reinterpret_cast<uintptr_t*>(js_calloc(sizeof(uintptr_t) * length_));
1846 if (!elems_) {
1847 return false;
1850 return true;
1853 template struct js::frontend::PreAllocateableGCArray<JSFunction*>;
1854 template struct js::frontend::PreAllocateableGCArray<js::Scope*>;
1856 void CompilationAtomCache::trace(JSTracer* trc) { atoms_.trace(trc); }
1858 void CompilationGCOutput::trace(JSTracer* trc) {
1859 TraceNullableRoot(trc, &script, "compilation-gc-output-script");
1860 TraceNullableRoot(trc, &module, "compilation-gc-output-module");
1861 TraceNullableRoot(trc, &sourceObject, "compilation-gc-output-source");
1862 functions.trace(trc);
1863 scopes.trace(trc);
1866 RegExpObject* RegExpStencil::createRegExp(
1867 JSContext* cx, const CompilationAtomCache& atomCache) const {
1868 Rooted<JSAtom*> atom(cx, atomCache.getExistingAtomAt(cx, atom_));
1869 return RegExpObject::createSyntaxChecked(cx, atom, flags(), TenuredObject);
1872 RegExpObject* RegExpStencil::createRegExpAndEnsureAtom(
1873 JSContext* cx, FrontendContext* fc, ParserAtomsTable& parserAtoms,
1874 CompilationAtomCache& atomCache) const {
1875 Rooted<JSAtom*> atom(cx, parserAtoms.toJSAtom(cx, fc, atom_, atomCache));
1876 if (!atom) {
1877 return nullptr;
1879 return RegExpObject::createSyntaxChecked(cx, atom, flags(), TenuredObject);
1882 AbstractScopePtr ScopeStencil::enclosing(
1883 CompilationState& compilationState) const {
1884 if (hasEnclosing()) {
1885 return AbstractScopePtr(compilationState, enclosing());
1888 return AbstractScopePtr::compilationEnclosingScope(compilationState);
1891 Scope* ScopeStencil::enclosingExistingScope(
1892 const CompilationInput& input, const CompilationGCOutput& gcOutput) const {
1893 if (hasEnclosing()) {
1894 Scope* result = gcOutput.getScopeNoBaseIndex(enclosing());
1895 MOZ_ASSERT(result, "Scope must already exist to use this method");
1896 return result;
1899 // When creating a scope based on the input and a gc-output, we assume that
1900 // the scope stencil that we are looking at has not been merged into another
1901 // stencil, and thus that we still have the compilation input of the stencil.
1903 // Otherwise, if this was in the case of an input generated from a Stencil
1904 // instead of live-gc values, we would not know its associated gcOutput as it
1905 // might not even have one yet.
1906 return input.enclosingScope.variant().as<Scope*>();
1909 Scope* ScopeStencil::createScope(JSContext* cx, CompilationInput& input,
1910 CompilationGCOutput& gcOutput,
1911 BaseParserScopeData* baseScopeData) const {
1912 Rooted<Scope*> enclosingScope(cx, enclosingExistingScope(input, gcOutput));
1913 return createScope(cx, input.atomCache, enclosingScope, baseScopeData);
1916 Scope* ScopeStencil::createScope(JSContext* cx, CompilationAtomCache& atomCache,
1917 Handle<Scope*> enclosingScope,
1918 BaseParserScopeData* baseScopeData) const {
1919 switch (kind()) {
1920 case ScopeKind::Function: {
1921 using ScopeType = FunctionScope;
1922 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1923 return createSpecificScope<ScopeType, CallObject>(
1924 cx, atomCache, enclosingScope, baseScopeData);
1926 case ScopeKind::Lexical:
1927 case ScopeKind::SimpleCatch:
1928 case ScopeKind::Catch:
1929 case ScopeKind::NamedLambda:
1930 case ScopeKind::StrictNamedLambda:
1931 case ScopeKind::FunctionLexical: {
1932 using ScopeType = LexicalScope;
1933 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1934 return createSpecificScope<ScopeType, BlockLexicalEnvironmentObject>(
1935 cx, atomCache, enclosingScope, baseScopeData);
1937 case ScopeKind::ClassBody: {
1938 using ScopeType = ClassBodyScope;
1939 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1940 return createSpecificScope<ScopeType, BlockLexicalEnvironmentObject>(
1941 cx, atomCache, enclosingScope, baseScopeData);
1943 case ScopeKind::FunctionBodyVar: {
1944 using ScopeType = VarScope;
1945 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1946 return createSpecificScope<ScopeType, VarEnvironmentObject>(
1947 cx, atomCache, enclosingScope, baseScopeData);
1949 case ScopeKind::Global:
1950 case ScopeKind::NonSyntactic: {
1951 using ScopeType = GlobalScope;
1952 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1953 return createSpecificScope<ScopeType, std::nullptr_t>(
1954 cx, atomCache, enclosingScope, baseScopeData);
1956 case ScopeKind::Eval:
1957 case ScopeKind::StrictEval: {
1958 using ScopeType = EvalScope;
1959 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1960 return createSpecificScope<ScopeType, VarEnvironmentObject>(
1961 cx, atomCache, enclosingScope, baseScopeData);
1963 case ScopeKind::Module: {
1964 using ScopeType = ModuleScope;
1965 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1966 return createSpecificScope<ScopeType, ModuleEnvironmentObject>(
1967 cx, atomCache, enclosingScope, baseScopeData);
1969 case ScopeKind::With: {
1970 using ScopeType = WithScope;
1971 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1972 return createSpecificScope<ScopeType, std::nullptr_t>(
1973 cx, atomCache, enclosingScope, baseScopeData);
1975 case ScopeKind::WasmFunction:
1976 case ScopeKind::WasmInstance: {
1977 // ScopeStencil does not support WASM
1978 break;
1981 MOZ_CRASH();
1984 bool CompilationState::prepareSharedDataStorage(FrontendContext* fc) {
1985 size_t allScriptCount = scriptData.length();
1986 size_t nonLazyScriptCount = nonLazyFunctionCount;
1987 if (!scriptData[0].isFunction()) {
1988 nonLazyScriptCount++;
1990 return sharedData.prepareStorageFor(fc, nonLazyScriptCount, allScriptCount);
1993 static bool CreateLazyScript(JSContext* cx,
1994 const CompilationAtomCache& atomCache,
1995 const CompilationStencil& stencil,
1996 CompilationGCOutput& gcOutput,
1997 const ScriptStencil& script,
1998 const ScriptStencilExtra& scriptExtra,
1999 ScriptIndex scriptIndex, HandleFunction function) {
2000 Rooted<ScriptSourceObject*> sourceObject(cx, gcOutput.sourceObject);
2002 size_t ngcthings = script.gcThingsLength;
2004 Rooted<BaseScript*> lazy(
2005 cx, BaseScript::CreateRawLazy(cx, ngcthings, function, sourceObject,
2006 scriptExtra.extent,
2007 scriptExtra.immutableFlags));
2008 if (!lazy) {
2009 return false;
2012 if (ngcthings) {
2013 if (!EmitScriptThingsVector(cx, atomCache, stencil, gcOutput,
2014 script.gcthings(stencil),
2015 lazy->gcthingsForInit())) {
2016 return false;
2020 if (scriptExtra.useMemberInitializers()) {
2021 lazy->setMemberInitializers(scriptExtra.memberInitializers());
2024 function->initScript(lazy);
2026 return true;
2029 // Parser-generated functions with the same prototype will share the same shape.
2030 // By computing the correct values up front, we can save a lot of time in the
2031 // Object creation code. For simplicity, we focus only on plain synchronous
2032 // functions which are by far the most common.
2034 // NOTE: Keep this in sync with `js::NewFunctionWithProto`.
2035 static JSFunction* CreateFunctionFast(JSContext* cx,
2036 CompilationAtomCache& atomCache,
2037 Handle<SharedShape*> shape,
2038 const ScriptStencil& script,
2039 const ScriptStencilExtra& scriptExtra) {
2040 MOZ_ASSERT(
2041 !scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsAsync));
2042 MOZ_ASSERT(!scriptExtra.immutableFlags.hasFlag(
2043 ImmutableScriptFlagsEnum::IsGenerator));
2044 MOZ_ASSERT(!script.functionFlags.isAsmJSNative());
2046 FunctionFlags flags = script.functionFlags;
2047 gc::AllocKind allocKind = flags.isExtended()
2048 ? gc::AllocKind::FUNCTION_EXTENDED
2049 : gc::AllocKind::FUNCTION;
2051 JSFunction* fun = JSFunction::create(cx, allocKind, gc::Heap::Tenured, shape);
2052 if (!fun) {
2053 return nullptr;
2056 fun->setArgCount(scriptExtra.nargs);
2057 fun->setFlags(flags);
2059 fun->initScript(nullptr);
2060 fun->initEnvironment(nullptr);
2062 if (script.functionAtom) {
2063 JSAtom* atom = atomCache.getExistingAtomAt(cx, script.functionAtom);
2064 MOZ_ASSERT(atom);
2065 fun->initAtom(atom);
2068 #ifdef DEBUG
2069 fun->assertFunctionKindIntegrity();
2070 #endif
2072 return fun;
2075 static JSFunction* CreateFunction(JSContext* cx,
2076 CompilationAtomCache& atomCache,
2077 const CompilationStencil& stencil,
2078 const ScriptStencil& script,
2079 const ScriptStencilExtra& scriptExtra,
2080 ScriptIndex functionIndex) {
2081 GeneratorKind generatorKind =
2082 scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsGenerator)
2083 ? GeneratorKind::Generator
2084 : GeneratorKind::NotGenerator;
2085 FunctionAsyncKind asyncKind =
2086 scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsAsync)
2087 ? FunctionAsyncKind::AsyncFunction
2088 : FunctionAsyncKind::SyncFunction;
2090 // Determine the new function's proto. This must be done for singleton
2091 // functions.
2092 RootedObject proto(cx);
2093 if (!GetFunctionPrototype(cx, generatorKind, asyncKind, &proto)) {
2094 return nullptr;
2097 gc::AllocKind allocKind = script.functionFlags.isExtended()
2098 ? gc::AllocKind::FUNCTION_EXTENDED
2099 : gc::AllocKind::FUNCTION;
2100 bool isAsmJS = script.functionFlags.isAsmJSNative();
2102 JSNative maybeNative = isAsmJS ? InstantiateAsmJS : nullptr;
2104 Rooted<JSAtom*> displayAtom(cx);
2105 if (script.functionAtom) {
2106 displayAtom.set(atomCache.getExistingAtomAt(cx, script.functionAtom));
2107 MOZ_ASSERT(displayAtom);
2109 RootedFunction fun(
2110 cx, NewFunctionWithProto(cx, maybeNative, scriptExtra.nargs,
2111 script.functionFlags, nullptr, displayAtom,
2112 proto, allocKind, TenuredObject));
2113 if (!fun) {
2114 return nullptr;
2117 if (isAsmJS) {
2118 RefPtr<const JS::WasmModule> asmJS =
2119 stencil.asmJS->moduleMap.lookup(functionIndex)->value();
2121 JSObject* moduleObj = asmJS->createObjectForAsmJS(cx);
2122 if (!moduleObj) {
2123 return nullptr;
2126 fun->setExtendedSlot(FunctionExtended::ASMJS_MODULE_SLOT,
2127 ObjectValue(*moduleObj));
2130 return fun;
2133 static bool InstantiateAtoms(JSContext* cx, FrontendContext* fc,
2134 CompilationAtomCache& atomCache,
2135 const CompilationStencil& stencil) {
2136 return InstantiateMarkedAtoms(cx, fc, stencil.parserAtomData, atomCache);
2139 static bool InstantiateScriptSourceObject(JSContext* cx,
2140 const JS::InstantiateOptions& options,
2141 const CompilationStencil& stencil,
2142 CompilationGCOutput& gcOutput) {
2143 MOZ_ASSERT(stencil.source);
2145 gcOutput.sourceObject = ScriptSourceObject::create(cx, stencil.source.get());
2146 if (!gcOutput.sourceObject) {
2147 return false;
2150 Rooted<ScriptSourceObject*> sourceObject(cx, gcOutput.sourceObject);
2151 if (!ScriptSourceObject::initFromOptions(cx, sourceObject, options)) {
2152 return false;
2155 return true;
2158 // Instantiate ModuleObject. Further initialization is done after the associated
2159 // BaseScript is instantiated in InstantiateTopLevel.
2160 static bool InstantiateModuleObject(JSContext* cx, FrontendContext* fc,
2161 CompilationAtomCache& atomCache,
2162 const CompilationStencil& stencil,
2163 CompilationGCOutput& gcOutput) {
2164 MOZ_ASSERT(stencil.isModule());
2166 gcOutput.module = ModuleObject::create(cx);
2167 if (!gcOutput.module) {
2168 return false;
2171 Rooted<ModuleObject*> module(cx, gcOutput.module);
2172 return stencil.moduleMetadata->initModule(cx, fc, atomCache, module);
2175 // Instantiate JSFunctions for each FunctionBox.
2176 static bool InstantiateFunctions(JSContext* cx, FrontendContext* fc,
2177 CompilationAtomCache& atomCache,
2178 const CompilationStencil& stencil,
2179 CompilationGCOutput& gcOutput) {
2180 using ImmutableFlags = ImmutableScriptFlagsEnum;
2182 MOZ_ASSERT(gcOutput.functions.length() == stencil.scriptData.size());
2184 // Most JSFunctions will be have the same Shape so we can compute it now to
2185 // allow fast object creation. Generators / Async will use the slow path
2186 // instead.
2187 Rooted<SharedShape*> functionShape(
2188 cx, GlobalObject::getFunctionShapeWithDefaultProto(
2189 cx, /* extended = */ false));
2190 if (!functionShape) {
2191 return false;
2194 Rooted<SharedShape*> extendedShape(
2195 cx, GlobalObject::getFunctionShapeWithDefaultProto(
2196 cx, /* extended = */ true));
2197 if (!extendedShape) {
2198 return false;
2201 for (auto item :
2202 CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
2203 const auto& scriptStencil = item.script;
2204 const auto& scriptExtra = (*item.scriptExtra);
2205 auto index = item.index;
2207 MOZ_ASSERT(!item.function);
2209 // Plain functions can use a fast path.
2210 bool useFastPath =
2211 !scriptExtra.immutableFlags.hasFlag(ImmutableFlags::IsAsync) &&
2212 !scriptExtra.immutableFlags.hasFlag(ImmutableFlags::IsGenerator) &&
2213 !scriptStencil.functionFlags.isAsmJSNative();
2215 JSFunction* fun;
2216 if (useFastPath) {
2217 Handle<SharedShape*> shape = scriptStencil.functionFlags.isExtended()
2218 ? extendedShape
2219 : functionShape;
2220 fun =
2221 CreateFunctionFast(cx, atomCache, shape, scriptStencil, scriptExtra);
2222 } else {
2223 fun = CreateFunction(cx, atomCache, stencil, scriptStencil, scriptExtra,
2224 index);
2227 if (!fun) {
2228 return false;
2231 // Self-hosted functions may have a canonical name to use when instantiating
2232 // into other realms.
2233 if (scriptStencil.hasSelfHostedCanonicalName()) {
2234 JSAtom* canonicalName = atomCache.getExistingAtomAt(
2235 cx, scriptStencil.selfHostedCanonicalName());
2236 fun->setAtom(canonicalName);
2239 gcOutput.getFunctionNoBaseIndex(index) = fun;
2242 return true;
2245 // Instantiate Scope for each ScopeStencil.
2247 // This should be called after InstantiateFunctions, given FunctionScope needs
2248 // associated JSFunction pointer, and also should be called before
2249 // InstantiateScriptStencils, given JSScript needs Scope pointer in gc things.
2250 static bool InstantiateScopes(JSContext* cx, CompilationInput& input,
2251 const CompilationStencil& stencil,
2252 CompilationGCOutput& gcOutput) {
2253 // While allocating Scope object from ScopeStencil, Scope object for the
2254 // enclosing Scope should already be allocated.
2256 // Enclosing scope of ScopeStencil can be either ScopeStencil or Scope*
2257 // pointer.
2259 // If the enclosing scope is ScopeStencil, it's guaranteed to be earlier
2260 // element in stencil.scopeData, because enclosing_ field holds
2261 // index into it, and newly created ScopeStencil is pushed back to the array.
2263 // If the enclosing scope is Scope*, it's CompilationInput.enclosingScope.
2265 MOZ_ASSERT(stencil.scopeData.size() == stencil.scopeNames.size());
2266 size_t scopeCount = stencil.scopeData.size();
2267 for (size_t i = 0; i < scopeCount; i++) {
2268 Scope* scope = stencil.scopeData[i].createScope(cx, input, gcOutput,
2269 stencil.scopeNames[i]);
2270 if (!scope) {
2271 return false;
2273 gcOutput.scopes[i] = scope;
2276 return true;
2279 // Instantiate js::BaseScripts from ScriptStencils for inner functions of the
2280 // compilation. Note that standalone functions and functions being delazified
2281 // are handled below with other top-levels.
2282 static bool InstantiateScriptStencils(JSContext* cx,
2283 CompilationAtomCache& atomCache,
2284 const CompilationStencil& stencil,
2285 CompilationGCOutput& gcOutput) {
2286 MOZ_ASSERT(stencil.isInitialStencil());
2288 Rooted<JSFunction*> fun(cx);
2289 for (auto item :
2290 CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
2291 auto& scriptStencil = item.script;
2292 auto* scriptExtra = item.scriptExtra;
2293 fun = item.function;
2294 auto index = item.index;
2295 if (scriptStencil.hasSharedData()) {
2296 // If the function was not referenced by enclosing script's bytecode, we
2297 // do not generate a BaseScript for it. For example, `(function(){});`.
2299 // `wasEmittedByEnclosingScript` is false also for standalone
2300 // functions. They are handled in InstantiateTopLevel.
2301 if (!scriptStencil.wasEmittedByEnclosingScript()) {
2302 continue;
2305 RootedScript script(
2306 cx, JSScript::fromStencil(cx, atomCache, stencil, gcOutput, index));
2307 if (!script) {
2308 return false;
2311 if (scriptStencil.allowRelazify()) {
2312 MOZ_ASSERT(script->isRelazifiable());
2313 script->setAllowRelazify();
2315 } else if (scriptStencil.functionFlags.isAsmJSNative()) {
2316 MOZ_ASSERT(fun->isAsmJSNative());
2317 } else {
2318 MOZ_ASSERT(fun->isIncomplete());
2319 if (!CreateLazyScript(cx, atomCache, stencil, gcOutput, scriptStencil,
2320 *scriptExtra, index, fun)) {
2321 return false;
2326 return true;
2329 // Instantiate the Stencil for the top-level script of the compilation. This
2330 // includes standalone functions and functions being delazified.
2331 static bool InstantiateTopLevel(JSContext* cx, CompilationInput& input,
2332 const CompilationStencil& stencil,
2333 CompilationGCOutput& gcOutput) {
2334 const ScriptStencil& scriptStencil =
2335 stencil.scriptData[CompilationStencil::TopLevelIndex];
2337 // Top-level asm.js does not generate a JSScript.
2338 if (scriptStencil.functionFlags.isAsmJSNative()) {
2339 return true;
2342 MOZ_ASSERT(scriptStencil.hasSharedData());
2343 MOZ_ASSERT(stencil.sharedData.get(CompilationStencil::TopLevelIndex));
2345 if (!stencil.isInitialStencil()) {
2346 MOZ_ASSERT(input.lazyOuterBaseScript());
2347 RootedScript script(cx,
2348 JSScript::CastFromLazy(input.lazyOuterBaseScript()));
2349 if (!JSScript::fullyInitFromStencil(cx, input.atomCache, stencil, gcOutput,
2350 script,
2351 CompilationStencil::TopLevelIndex)) {
2352 return false;
2355 if (scriptStencil.allowRelazify()) {
2356 MOZ_ASSERT(script->isRelazifiable());
2357 script->setAllowRelazify();
2360 gcOutput.script = script;
2361 return true;
2364 gcOutput.script =
2365 JSScript::fromStencil(cx, input.atomCache, stencil, gcOutput,
2366 CompilationStencil::TopLevelIndex);
2367 if (!gcOutput.script) {
2368 return false;
2371 if (scriptStencil.allowRelazify()) {
2372 MOZ_ASSERT(gcOutput.script->isRelazifiable());
2373 gcOutput.script->setAllowRelazify();
2376 const ScriptStencilExtra& scriptExtra =
2377 stencil.scriptExtra[CompilationStencil::TopLevelIndex];
2379 // Finish initializing the ModuleObject if needed.
2380 if (scriptExtra.isModule()) {
2381 RootedScript script(cx, gcOutput.script);
2382 Rooted<ModuleObject*> module(cx, gcOutput.module);
2384 script->outermostScope()->as<ModuleScope>().initModule(module);
2386 module->initScriptSlots(script);
2388 if (!ModuleObject::createEnvironment(cx, module)) {
2389 return false;
2392 if (!ModuleObject::Freeze(cx, module)) {
2393 return false;
2397 return true;
2400 // When a function is first referenced by enclosing script's bytecode, we need
2401 // to update it with information determined by the BytecodeEmitter. This applies
2402 // to both initial and delazification parses. The functions being update may or
2403 // may not have bytecode at this point.
2404 static void UpdateEmittedInnerFunctions(JSContext* cx,
2405 CompilationAtomCache& atomCache,
2406 const CompilationStencil& stencil,
2407 CompilationGCOutput& gcOutput) {
2408 for (auto item :
2409 CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
2410 auto& scriptStencil = item.script;
2411 auto& fun = item.function;
2412 if (!scriptStencil.wasEmittedByEnclosingScript()) {
2413 continue;
2416 if (scriptStencil.functionFlags.isAsmJSNative() ||
2417 fun->baseScript()->hasBytecode()) {
2418 // Non-lazy inner functions don't use the enclosingScope_ field.
2419 MOZ_ASSERT(!scriptStencil.hasLazyFunctionEnclosingScopeIndex());
2420 } else {
2421 // Apply updates from FunctionEmitter::emitLazy().
2422 BaseScript* script = fun->baseScript();
2424 ScopeIndex index = scriptStencil.lazyFunctionEnclosingScopeIndex();
2425 Scope* scope = gcOutput.getScopeNoBaseIndex(index);
2426 script->setEnclosingScope(scope);
2428 // Inferred and Guessed names are computed by BytecodeEmitter and so may
2429 // need to be applied to existing JSFunctions during delazification.
2430 if (fun->fullDisplayAtom() == nullptr) {
2431 JSAtom* funcAtom = nullptr;
2432 if (scriptStencil.functionFlags.hasInferredName() ||
2433 scriptStencil.functionFlags.hasGuessedAtom()) {
2434 funcAtom =
2435 atomCache.getExistingAtomAt(cx, scriptStencil.functionAtom);
2436 MOZ_ASSERT(funcAtom);
2438 if (scriptStencil.functionFlags.hasInferredName()) {
2439 fun->setInferredName(funcAtom);
2441 if (scriptStencil.functionFlags.hasGuessedAtom()) {
2442 fun->setGuessedAtom(funcAtom);
2449 // During initial parse we must link lazy-functions-inside-lazy-functions to
2450 // their enclosing script.
2451 static void LinkEnclosingLazyScript(const CompilationStencil& stencil,
2452 CompilationGCOutput& gcOutput) {
2453 for (auto item :
2454 CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
2455 auto& scriptStencil = item.script;
2456 auto& fun = item.function;
2457 if (!scriptStencil.functionFlags.hasBaseScript()) {
2458 continue;
2461 if (!fun->baseScript()) {
2462 continue;
2465 if (fun->baseScript()->hasBytecode()) {
2466 continue;
2469 BaseScript* script = fun->baseScript();
2470 MOZ_ASSERT(!script->hasBytecode());
2472 for (auto inner : script->gcthings()) {
2473 if (!inner.is<JSObject>()) {
2474 continue;
2476 JSFunction* innerFun = &inner.as<JSObject>().as<JSFunction>();
2478 MOZ_ASSERT(innerFun->hasBaseScript(),
2479 "inner function should have base script");
2480 if (!innerFun->hasBaseScript()) {
2481 continue;
2484 // Check for the case that the inner function has the base script flag,
2485 // but still doesn't have the actual base script pointer.
2486 // `baseScript` method asserts the pointer itself, so no extra MOZ_ASSERT
2487 // here.
2488 if (!innerFun->baseScript()) {
2489 continue;
2492 innerFun->setEnclosingLazyScript(script);
2497 #ifdef DEBUG
2498 // Some fields aren't used in delazification, given the target functions and
2499 // scripts are already instantiated, but they still should match.
2500 static void AssertDelazificationFieldsMatch(const CompilationStencil& stencil,
2501 CompilationGCOutput& gcOutput) {
2502 for (auto item :
2503 CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
2504 auto& scriptStencil = item.script;
2505 auto* scriptExtra = item.scriptExtra;
2506 auto& fun = item.function;
2508 MOZ_ASSERT(scriptExtra == nullptr);
2510 // Names are updated by UpdateInnerFunctions.
2511 constexpr uint16_t HAS_INFERRED_NAME =
2512 uint16_t(FunctionFlags::Flags::HAS_INFERRED_NAME);
2513 constexpr uint16_t HAS_GUESSED_ATOM =
2514 uint16_t(FunctionFlags::Flags::HAS_GUESSED_ATOM);
2515 constexpr uint16_t MUTABLE_FLAGS =
2516 uint16_t(FunctionFlags::Flags::MUTABLE_FLAGS);
2517 constexpr uint16_t acceptableDifferenceForFunction =
2518 HAS_INFERRED_NAME | HAS_GUESSED_ATOM | MUTABLE_FLAGS;
2520 MOZ_ASSERT((fun->flags().toRaw() | acceptableDifferenceForFunction) ==
2521 (scriptStencil.functionFlags.toRaw() |
2522 acceptableDifferenceForFunction));
2524 // Delazification shouldn't delazify inner scripts.
2525 MOZ_ASSERT_IF(item.index == CompilationStencil::TopLevelIndex,
2526 scriptStencil.hasSharedData());
2527 MOZ_ASSERT_IF(item.index > CompilationStencil::TopLevelIndex,
2528 !scriptStencil.hasSharedData());
2531 #endif // DEBUG
2533 // When delazifying, use the existing JSFunctions. The initial and delazifying
2534 // parse are required to generate the same sequence of functions for lazy
2535 // parsing to work at all.
2536 static void FunctionsFromExistingLazy(CompilationInput& input,
2537 CompilationGCOutput& gcOutput) {
2538 MOZ_ASSERT(!gcOutput.functions[0]);
2540 size_t instantiatedFunIndex = 0;
2541 gcOutput.functions[instantiatedFunIndex++] = input.function();
2543 for (JS::GCCellPtr elem : input.lazyOuterBaseScript()->gcthings()) {
2544 if (!elem.is<JSObject>()) {
2545 continue;
2547 JSFunction* fun = &elem.as<JSObject>().as<JSFunction>();
2548 gcOutput.functions[instantiatedFunIndex++] = fun;
2552 void CompilationStencil::borrowFromExtensibleCompilationStencil(
2553 ExtensibleCompilationStencil& extensibleStencil) {
2554 canLazilyParse = extensibleStencil.canLazilyParse;
2555 functionKey = extensibleStencil.functionKey;
2557 // Borrow the vector content as span.
2558 scriptData = extensibleStencil.scriptData;
2559 scriptExtra = extensibleStencil.scriptExtra;
2561 gcThingData = extensibleStencil.gcThingData;
2563 scopeData = extensibleStencil.scopeData;
2564 scopeNames = extensibleStencil.scopeNames;
2566 regExpData = extensibleStencil.regExpData;
2567 bigIntData = extensibleStencil.bigIntData;
2568 objLiteralData = extensibleStencil.objLiteralData;
2570 // Borrow the parser atoms as span.
2571 parserAtomData = extensibleStencil.parserAtoms.entries_;
2573 // Borrow container.
2574 sharedData.setBorrow(&extensibleStencil.sharedData);
2576 // Share ref-counted data.
2577 source = extensibleStencil.source;
2578 asmJS = extensibleStencil.asmJS;
2579 moduleMetadata = extensibleStencil.moduleMetadata;
2582 #ifdef DEBUG
2583 void CompilationStencil::assertBorrowingFromExtensibleCompilationStencil(
2584 const ExtensibleCompilationStencil& extensibleStencil) const {
2585 MOZ_ASSERT(canLazilyParse == extensibleStencil.canLazilyParse);
2586 MOZ_ASSERT(functionKey == extensibleStencil.functionKey);
2588 AssertBorrowingSpan(scriptData, extensibleStencil.scriptData);
2589 AssertBorrowingSpan(scriptExtra, extensibleStencil.scriptExtra);
2591 AssertBorrowingSpan(gcThingData, extensibleStencil.gcThingData);
2593 AssertBorrowingSpan(scopeData, extensibleStencil.scopeData);
2594 AssertBorrowingSpan(scopeNames, extensibleStencil.scopeNames);
2596 AssertBorrowingSpan(regExpData, extensibleStencil.regExpData);
2597 AssertBorrowingSpan(bigIntData, extensibleStencil.bigIntData);
2598 AssertBorrowingSpan(objLiteralData, extensibleStencil.objLiteralData);
2600 AssertBorrowingSpan(parserAtomData, extensibleStencil.parserAtoms.entries_);
2602 MOZ_ASSERT(sharedData.isBorrow());
2603 MOZ_ASSERT(sharedData.asBorrow() == &extensibleStencil.sharedData);
2605 MOZ_ASSERT(source == extensibleStencil.source);
2606 MOZ_ASSERT(asmJS == extensibleStencil.asmJS);
2607 MOZ_ASSERT(moduleMetadata == extensibleStencil.moduleMetadata);
2609 #endif
2611 CompilationStencil::CompilationStencil(
2612 UniquePtr<ExtensibleCompilationStencil>&& extensibleStencil)
2613 : alloc(LifoAllocChunkSize) {
2614 ownedBorrowStencil = std::move(extensibleStencil);
2616 storageType = StorageType::OwnedExtensible;
2618 borrowFromExtensibleCompilationStencil(*ownedBorrowStencil);
2620 #ifdef DEBUG
2621 assertNoExternalDependency();
2622 #endif
2625 /* static */
2626 bool CompilationStencil::instantiateStencils(JSContext* cx,
2627 CompilationInput& input,
2628 const CompilationStencil& stencil,
2629 CompilationGCOutput& gcOutput) {
2630 AutoReportFrontendContext fc(cx);
2631 if (!prepareForInstantiate(&fc, input.atomCache, stencil, gcOutput)) {
2632 return false;
2635 return instantiateStencilAfterPreparation(cx, input, stencil, gcOutput);
2638 /* static */
2639 bool CompilationStencil::instantiateStencilAfterPreparation(
2640 JSContext* cx, CompilationInput& input, const CompilationStencil& stencil,
2641 CompilationGCOutput& gcOutput) {
2642 // Distinguish between the initial (possibly lazy) compile and any subsequent
2643 // delazification compiles. Delazification will update existing GC things.
2644 bool isInitialParse = stencil.isInitialStencil();
2645 MOZ_ASSERT(stencil.isInitialStencil() == input.isInitialStencil());
2647 // Assert the consistency between the compile option and the target global.
2648 MOZ_ASSERT_IF(cx->realm()->behaviors().discardSource(),
2649 !stencil.canLazilyParse);
2651 CompilationAtomCache& atomCache = input.atomCache;
2652 const JS::InstantiateOptions options(input.options);
2654 // Phase 1: Instantiate JSAtom/JSStrings.
2655 AutoReportFrontendContext fc(cx);
2656 if (!InstantiateAtoms(cx, &fc, atomCache, stencil)) {
2657 return false;
2660 // Phase 2: Instantiate ScriptSourceObject, ModuleObject, JSFunctions.
2661 if (isInitialParse) {
2662 if (!InstantiateScriptSourceObject(cx, options, stencil, gcOutput)) {
2663 return false;
2666 if (stencil.moduleMetadata) {
2667 // The enclosing script of a module is always the global scope. Fetch the
2668 // scope of the current global and update input data.
2669 MOZ_ASSERT(input.enclosingScope.isNull());
2670 input.enclosingScope = InputScope(&cx->global()->emptyGlobalScope());
2671 MOZ_ASSERT(input.enclosingScope.environmentChainLength() ==
2672 ModuleScope::EnclosingEnvironmentChainLength);
2674 if (!InstantiateModuleObject(cx, &fc, atomCache, stencil, gcOutput)) {
2675 return false;
2679 if (!InstantiateFunctions(cx, &fc, atomCache, stencil, gcOutput)) {
2680 return false;
2682 } else {
2683 MOZ_ASSERT(
2684 stencil.scriptData[CompilationStencil::TopLevelIndex].isFunction());
2686 // FunctionKey is used when caching to map a delazification stencil to a
2687 // specific lazy script. It is not used by instantiation, but we should
2688 // ensure it is correctly defined.
2689 MOZ_ASSERT(stencil.functionKey == input.extent().toFunctionKey());
2691 FunctionsFromExistingLazy(input, gcOutput);
2692 MOZ_ASSERT(gcOutput.functions.length() == stencil.scriptData.size());
2694 #ifdef DEBUG
2695 AssertDelazificationFieldsMatch(stencil, gcOutput);
2696 #endif
2699 // Phase 3: Instantiate js::Scopes.
2700 if (!InstantiateScopes(cx, input, stencil, gcOutput)) {
2701 return false;
2704 // Phase 4: Instantiate (inner) BaseScripts.
2705 if (isInitialParse) {
2706 if (!InstantiateScriptStencils(cx, atomCache, stencil, gcOutput)) {
2707 return false;
2711 // Phase 5: Finish top-level handling
2712 if (!InstantiateTopLevel(cx, input, stencil, gcOutput)) {
2713 return false;
2716 // !! Must be infallible from here forward !!
2718 // Phase 6: Update lazy scripts.
2719 if (stencil.canLazilyParse) {
2720 UpdateEmittedInnerFunctions(cx, atomCache, stencil, gcOutput);
2722 if (isInitialParse) {
2723 LinkEnclosingLazyScript(stencil, gcOutput);
2727 return true;
2730 // The top-level self-hosted script is created and executed in each realm that
2731 // needs it. While the stencil has a gcthings list for the various top-level
2732 // functions, we use special machinery to create them on demand. So instead we
2733 // use a placeholder JSFunction that should never be called.
2734 static bool SelfHostedDummyFunction(JSContext* cx, unsigned argc,
2735 JS::Value* vp) {
2736 MOZ_CRASH("Self-hosting top-level should not use functions directly");
2739 bool CompilationStencil::instantiateSelfHostedAtoms(
2740 JSContext* cx, AtomSet& atomSet, CompilationAtomCache& atomCache) const {
2741 MOZ_ASSERT(isInitialStencil());
2743 // We must instantiate atoms during startup so they can be made permanent
2744 // across multiple runtimes.
2745 AutoReportFrontendContext fc(cx);
2746 return InstantiateMarkedAtomsAsPermanent(cx, &fc, atomSet, parserAtomData,
2747 atomCache);
2750 JSScript* CompilationStencil::instantiateSelfHostedTopLevelForRealm(
2751 JSContext* cx, CompilationInput& input) {
2752 MOZ_ASSERT(isInitialStencil());
2754 Rooted<CompilationGCOutput> gcOutput(cx);
2756 gcOutput.get().sourceObject = SelfHostingScriptSourceObject(cx);
2757 if (!gcOutput.get().sourceObject) {
2758 return nullptr;
2761 // The top-level script has ScriptIndex references in its gcthings list, but
2762 // we do not want to instantiate those functions here since they are instead
2763 // created on demand from the stencil. Create a dummy function and populate
2764 // the functions array of the CompilationGCOutput with references to it.
2765 RootedFunction dummy(
2766 cx, NewNativeFunction(cx, SelfHostedDummyFunction, 0, nullptr));
2767 if (!dummy) {
2768 return nullptr;
2771 if (!gcOutput.get().functions.allocateWith(dummy, scriptData.size())) {
2772 ReportOutOfMemory(cx);
2773 return nullptr;
2776 if (!InstantiateTopLevel(cx, input, *this, gcOutput.get())) {
2777 return nullptr;
2780 return gcOutput.get().script;
2783 JSFunction* CompilationStencil::instantiateSelfHostedLazyFunction(
2784 JSContext* cx, CompilationAtomCache& atomCache, ScriptIndex index,
2785 Handle<JSAtom*> name) {
2786 GeneratorKind generatorKind = scriptExtra[index].immutableFlags.hasFlag(
2787 ImmutableScriptFlagsEnum::IsGenerator)
2788 ? GeneratorKind::Generator
2789 : GeneratorKind::NotGenerator;
2790 FunctionAsyncKind asyncKind = scriptExtra[index].immutableFlags.hasFlag(
2791 ImmutableScriptFlagsEnum::IsAsync)
2792 ? FunctionAsyncKind::AsyncFunction
2793 : FunctionAsyncKind::SyncFunction;
2795 Rooted<JSAtom*> funName(cx);
2796 if (scriptData[index].hasSelfHostedCanonicalName()) {
2797 // SetCanonicalName was used to override the name.
2798 funName = atomCache.getExistingAtomAt(
2799 cx, scriptData[index].selfHostedCanonicalName());
2800 } else if (name) {
2801 // Our caller has a name it wants to use.
2802 funName = name;
2803 } else {
2804 MOZ_ASSERT(scriptData[index].functionAtom);
2805 funName = atomCache.getExistingAtomAt(cx, scriptData[index].functionAtom);
2808 RootedObject proto(cx);
2809 if (!GetFunctionPrototype(cx, generatorKind, asyncKind, &proto)) {
2810 return nullptr;
2813 RootedObject env(cx, &cx->global()->lexicalEnvironment());
2815 RootedFunction fun(
2817 NewFunctionWithProto(cx, nullptr, scriptExtra[index].nargs,
2818 scriptData[index].functionFlags, env, funName, proto,
2819 gc::AllocKind::FUNCTION_EXTENDED, TenuredObject));
2820 if (!fun) {
2821 return nullptr;
2824 fun->initSelfHostedLazyScript(&cx->runtime()->selfHostedLazyScript.ref());
2826 JSAtom* selfHostedName =
2827 atomCache.getExistingAtomAt(cx, scriptData[index].functionAtom);
2828 SetClonedSelfHostedFunctionName(fun, selfHostedName->asPropertyName());
2830 return fun;
2833 bool CompilationStencil::delazifySelfHostedFunction(
2834 JSContext* cx, CompilationAtomCache& atomCache, ScriptIndexRange range,
2835 HandleFunction fun) {
2836 // Determine the equivalent ScopeIndex range by looking at the outermost scope
2837 // of the scripts defining the range. Take special care if this is the last
2838 // script in the list.
2839 auto getOutermostScope = [this](ScriptIndex scriptIndex) -> ScopeIndex {
2840 MOZ_ASSERT(scriptData[scriptIndex].hasSharedData());
2841 auto gcthings = scriptData[scriptIndex].gcthings(*this);
2842 return gcthings[GCThingIndex::outermostScopeIndex()].toScope();
2844 ScopeIndex scopeIndex = getOutermostScope(range.start);
2845 ScopeIndex scopeLimit = (range.limit < scriptData.size())
2846 ? getOutermostScope(range.limit)
2847 : ScopeIndex(scopeData.size());
2849 // Prepare to instantiate by allocating the output arrays. We also set a base
2850 // index to avoid allocations in most cases.
2851 AutoReportFrontendContext fc(cx);
2852 Rooted<CompilationGCOutput> gcOutput(cx);
2853 if (!gcOutput.get().ensureAllocatedWithBaseIndex(
2854 &fc, range.start, range.limit, scopeIndex, scopeLimit)) {
2855 return false;
2858 // Phase 1: Instantiate JSAtoms.
2859 // NOTE: The self-hosted atoms are all "permanent" and the
2860 // CompilationAtomCache is already stored on the JSRuntime.
2862 // Phase 2: Instantiate ScriptSourceObject, ModuleObject, JSFunctions.
2864 // Get the corresponding ScriptSourceObject to use in current realm.
2865 gcOutput.get().sourceObject = SelfHostingScriptSourceObject(cx);
2866 if (!gcOutput.get().sourceObject) {
2867 return false;
2870 size_t instantiatedFunIndex = 0;
2872 // Delazification target function.
2873 gcOutput.get().functions[instantiatedFunIndex++] = fun;
2875 // Allocate inner functions. Self-hosted functions do not allocate these with
2876 // the initial function.
2877 for (size_t i = range.start + 1; i < range.limit; i++) {
2878 JSFunction* innerFun = CreateFunction(cx, atomCache, *this, scriptData[i],
2879 scriptExtra[i], ScriptIndex(i));
2880 if (!innerFun) {
2881 return false;
2883 gcOutput.get().functions[instantiatedFunIndex++] = innerFun;
2886 // Phase 3: Instantiate js::Scopes.
2887 // NOTE: When the enclosing scope is not a stencil, directly use the
2888 // `emptyGlobalScope` instead of reading from CompilationInput. This is
2889 // a special case for self-hosted delazification that allows us to reuse
2890 // the CompilationInput between different realms.
2891 size_t instantiatedScopeIndex = 0;
2892 for (size_t i = scopeIndex; i < scopeLimit; i++) {
2893 ScopeStencil& data = scopeData[i];
2894 Rooted<Scope*> enclosingScope(
2895 cx, data.hasEnclosing() ? gcOutput.get().getScope(data.enclosing())
2896 : &cx->global()->emptyGlobalScope());
2898 js::Scope* scope =
2899 data.createScope(cx, atomCache, enclosingScope, scopeNames[i]);
2900 if (!scope) {
2901 return false;
2903 gcOutput.get().scopes[instantiatedScopeIndex++] = scope;
2906 // Phase 4: Instantiate (inner) BaseScripts.
2907 ScriptIndex innerStart(range.start + 1);
2908 for (size_t i = innerStart; i < range.limit; i++) {
2909 if (!JSScript::fromStencil(cx, atomCache, *this, gcOutput.get(),
2910 ScriptIndex(i))) {
2911 return false;
2915 // Phase 5: Finish top-level handling
2916 // NOTE: We do not have a `CompilationInput` handy here, so avoid using the
2917 // `InstantiateTopLevel` helper and directly create the JSScript. Our
2918 // caller also handles the `AllowRelazify` flag for us since self-hosted
2919 // delazification is a special case.
2920 if (!JSScript::fromStencil(cx, atomCache, *this, gcOutput.get(),
2921 range.start)) {
2922 return false;
2925 // Phase 6: Update lazy scripts.
2926 // NOTE: Self-hosting is always fully parsed so there is nothing to do here.
2928 return true;
2931 /* static */
2932 bool CompilationStencil::prepareForInstantiate(
2933 FrontendContext* fc, CompilationAtomCache& atomCache,
2934 const CompilationStencil& stencil, CompilationGCOutput& gcOutput) {
2935 // Allocate the `gcOutput` arrays.
2936 if (!gcOutput.ensureAllocated(fc, stencil.scriptData.size(),
2937 stencil.scopeData.size())) {
2938 return false;
2941 return atomCache.allocate(fc, stencil.parserAtomData.size());
2944 /* static */
2945 bool CompilationStencil::prepareForInstantiate(
2946 FrontendContext* fc, const CompilationStencil& stencil,
2947 PreallocatedCompilationGCOutput& gcOutput) {
2948 return gcOutput.allocate(fc, stencil.scriptData.size(),
2949 stencil.scopeData.size());
2952 bool CompilationStencil::serializeStencils(JSContext* cx,
2953 CompilationInput& input,
2954 JS::TranscodeBuffer& buf,
2955 bool* succeededOut) const {
2956 if (succeededOut) {
2957 *succeededOut = false;
2959 AutoReportFrontendContext fc(cx);
2960 XDRStencilEncoder encoder(&fc, buf);
2962 XDRResult res = encoder.codeStencil(*this);
2963 if (res.isErr()) {
2964 if (JS::IsTranscodeFailureResult(res.unwrapErr())) {
2965 buf.clear();
2966 return true;
2968 MOZ_ASSERT(res.unwrapErr() == JS::TranscodeResult::Throw);
2970 return false;
2973 if (succeededOut) {
2974 *succeededOut = true;
2976 return true;
2979 bool CompilationStencil::deserializeStencils(
2980 FrontendContext* fc, const JS::ReadOnlyCompileOptions& compileOptions,
2981 const JS::TranscodeRange& range, bool* succeededOut) {
2982 if (succeededOut) {
2983 *succeededOut = false;
2985 MOZ_ASSERT(parserAtomData.empty());
2986 XDRStencilDecoder decoder(fc, range);
2987 JS::DecodeOptions options(compileOptions);
2989 XDRResult res = decoder.codeStencil(options, *this);
2990 if (res.isErr()) {
2991 if (JS::IsTranscodeFailureResult(res.unwrapErr())) {
2992 return true;
2994 MOZ_ASSERT(res.unwrapErr() == JS::TranscodeResult::Throw);
2996 return false;
2999 if (succeededOut) {
3000 *succeededOut = true;
3002 return true;
3005 ExtensibleCompilationStencil::ExtensibleCompilationStencil(ScriptSource* source)
3006 : alloc(CompilationStencil::LifoAllocChunkSize),
3007 source(source),
3008 parserAtoms(alloc) {}
3010 ExtensibleCompilationStencil::ExtensibleCompilationStencil(
3011 CompilationInput& input)
3012 : canLazilyParse(CanLazilyParse(input.options)),
3013 alloc(CompilationStencil::LifoAllocChunkSize),
3014 source(input.source),
3015 parserAtoms(alloc) {}
3017 ExtensibleCompilationStencil::ExtensibleCompilationStencil(
3018 const JS::ReadOnlyCompileOptions& options, RefPtr<ScriptSource> source)
3019 : canLazilyParse(CanLazilyParse(options)),
3020 alloc(CompilationStencil::LifoAllocChunkSize),
3021 source(std::move(source)),
3022 parserAtoms(alloc) {}
3024 CompilationState::CompilationState(FrontendContext* fc,
3025 LifoAllocScope& parserAllocScope,
3026 CompilationInput& input)
3027 : ExtensibleCompilationStencil(input),
3028 directives(input.options.forceStrictMode()),
3029 usedNames(fc),
3030 parserAllocScope(parserAllocScope),
3031 input(input) {}
3033 BorrowingCompilationStencil::BorrowingCompilationStencil(
3034 ExtensibleCompilationStencil& extensibleStencil)
3035 : CompilationStencil(extensibleStencil.source) {
3036 storageType = StorageType::Borrowed;
3038 borrowFromExtensibleCompilationStencil(extensibleStencil);
3041 SharedDataContainer::~SharedDataContainer() {
3042 if (isEmpty()) {
3043 // Nothing to do.
3044 } else if (isSingle()) {
3045 asSingle()->Release();
3046 } else if (isVector()) {
3047 js_delete(asVector());
3048 } else if (isMap()) {
3049 js_delete(asMap());
3050 } else {
3051 MOZ_ASSERT(isBorrow());
3052 // Nothing to do.
3056 bool SharedDataContainer::initVector(FrontendContext* fc) {
3057 MOZ_ASSERT(isEmpty());
3059 auto* vec = js_new<SharedDataVector>();
3060 if (!vec) {
3061 ReportOutOfMemory(fc);
3062 return false;
3064 data_ = uintptr_t(vec) | VectorTag;
3065 return true;
3068 bool SharedDataContainer::initMap(FrontendContext* fc) {
3069 MOZ_ASSERT(isEmpty());
3071 auto* map = js_new<SharedDataMap>();
3072 if (!map) {
3073 ReportOutOfMemory(fc);
3074 return false;
3076 data_ = uintptr_t(map) | MapTag;
3077 return true;
3080 bool SharedDataContainer::prepareStorageFor(FrontendContext* fc,
3081 size_t nonLazyScriptCount,
3082 size_t allScriptCount) {
3083 MOZ_ASSERT(isEmpty());
3085 if (nonLazyScriptCount <= 1) {
3086 MOZ_ASSERT(isSingle());
3087 return true;
3090 // If the ratio of scripts with bytecode is small, allocating the Vector
3091 // storage with the number of all scripts isn't space-efficient.
3092 // In that case use HashMap instead.
3094 // In general, we expect either all scripts to contain bytecode (priviledge
3095 // and self-hosted), or almost none to (eg standard lazy parsing output).
3096 constexpr size_t thresholdRatio = 8;
3097 bool useHashMap = nonLazyScriptCount < allScriptCount / thresholdRatio;
3098 if (useHashMap) {
3099 if (!initMap(fc)) {
3100 return false;
3102 if (!asMap()->reserve(nonLazyScriptCount)) {
3103 ReportOutOfMemory(fc);
3104 return false;
3106 } else {
3107 if (!initVector(fc)) {
3108 return false;
3110 if (!asVector()->resize(allScriptCount)) {
3111 ReportOutOfMemory(fc);
3112 return false;
3116 return true;
3119 bool SharedDataContainer::cloneFrom(FrontendContext* fc,
3120 const SharedDataContainer& other) {
3121 MOZ_ASSERT(isEmpty());
3123 if (other.isBorrow()) {
3124 return cloneFrom(fc, *other.asBorrow());
3127 if (other.isSingle()) {
3128 // As we clone, we add an extra reference.
3129 RefPtr<SharedImmutableScriptData> ref(other.asSingle());
3130 setSingle(ref.forget());
3131 } else if (other.isVector()) {
3132 if (!initVector(fc)) {
3133 return false;
3135 if (!asVector()->appendAll(*other.asVector())) {
3136 ReportOutOfMemory(fc);
3137 return false;
3139 } else if (other.isMap()) {
3140 if (!initMap(fc)) {
3141 return false;
3143 auto& otherMap = *other.asMap();
3144 if (!asMap()->reserve(otherMap.count())) {
3145 ReportOutOfMemory(fc);
3146 return false;
3148 auto& map = *asMap();
3149 for (auto iter = otherMap.iter(); !iter.done(); iter.next()) {
3150 auto& entry = iter.get();
3151 map.putNewInfallible(entry.key(), entry.value());
3154 return true;
3157 js::SharedImmutableScriptData* SharedDataContainer::get(
3158 ScriptIndex index) const {
3159 if (isSingle()) {
3160 if (index == CompilationStencil::TopLevelIndex) {
3161 return asSingle();
3163 return nullptr;
3166 if (isVector()) {
3167 auto& vec = *asVector();
3168 if (index.index < vec.length()) {
3169 return vec[index];
3171 return nullptr;
3174 if (isMap()) {
3175 auto& map = *asMap();
3176 auto p = map.lookup(index);
3177 if (p) {
3178 return p->value();
3180 return nullptr;
3183 MOZ_ASSERT(isBorrow());
3184 return asBorrow()->get(index);
3187 bool SharedDataContainer::convertFromSingleToMap(FrontendContext* fc) {
3188 MOZ_ASSERT(isSingle());
3190 // Use a temporary container so that on OOM we do not break the stencil.
3191 SharedDataContainer other;
3192 if (!other.initMap(fc)) {
3193 return false;
3196 if (!other.asMap()->putNew(CompilationStencil::TopLevelIndex, asSingle())) {
3197 ReportOutOfMemory(fc);
3198 return false;
3201 std::swap(data_, other.data_);
3202 return true;
3205 bool SharedDataContainer::addAndShare(FrontendContext* fc, ScriptIndex index,
3206 js::SharedImmutableScriptData* data) {
3207 MOZ_ASSERT(!isBorrow());
3209 if (isSingle()) {
3210 MOZ_ASSERT(index == CompilationStencil::TopLevelIndex);
3211 RefPtr<SharedImmutableScriptData> ref(data);
3212 if (!SharedImmutableScriptData::shareScriptData(fc, ref)) {
3213 return false;
3215 setSingle(ref.forget());
3216 return true;
3219 if (isVector()) {
3220 auto& vec = *asVector();
3221 // Resized by SharedDataContainer::prepareStorageFor.
3222 vec[index] = data;
3223 return SharedImmutableScriptData::shareScriptData(fc, vec[index]);
3226 MOZ_ASSERT(isMap());
3227 auto& map = *asMap();
3228 // Reserved by SharedDataContainer::prepareStorageFor.
3229 map.putNewInfallible(index, data);
3230 auto p = map.lookup(index);
3231 MOZ_ASSERT(p);
3232 return SharedImmutableScriptData::shareScriptData(fc, p->value());
3235 bool SharedDataContainer::addExtraWithoutShare(
3236 FrontendContext* fc, ScriptIndex index,
3237 js::SharedImmutableScriptData* data) {
3238 MOZ_ASSERT(!isEmpty());
3240 if (isSingle()) {
3241 if (!convertFromSingleToMap(fc)) {
3242 return false;
3246 if (isVector()) {
3247 // SharedDataContainer::prepareStorageFor allocates space for all scripts.
3248 (*asVector())[index] = data;
3249 return true;
3252 MOZ_ASSERT(isMap());
3253 // SharedDataContainer::prepareStorageFor doesn't allocate space for
3254 // delazification, and this can fail.
3255 if (!asMap()->putNew(index, data)) {
3256 ReportOutOfMemory(fc);
3257 return false;
3259 return true;
3262 #ifdef DEBUG
3263 void CompilationStencil::assertNoExternalDependency() const {
3264 if (ownedBorrowStencil) {
3265 ownedBorrowStencil->assertNoExternalDependency();
3267 assertBorrowingFromExtensibleCompilationStencil(*ownedBorrowStencil);
3268 return;
3271 MOZ_ASSERT_IF(!scriptData.empty(), alloc.contains(scriptData.data()));
3272 MOZ_ASSERT_IF(!scriptExtra.empty(), alloc.contains(scriptExtra.data()));
3274 MOZ_ASSERT_IF(!scopeData.empty(), alloc.contains(scopeData.data()));
3275 MOZ_ASSERT_IF(!scopeNames.empty(), alloc.contains(scopeNames.data()));
3276 for (const auto* data : scopeNames) {
3277 MOZ_ASSERT_IF(data, alloc.contains(data));
3280 MOZ_ASSERT_IF(!regExpData.empty(), alloc.contains(regExpData.data()));
3282 MOZ_ASSERT_IF(!bigIntData.empty(), alloc.contains(bigIntData.data()));
3283 for (const auto& data : bigIntData) {
3284 MOZ_ASSERT(data.isContainedIn(alloc));
3287 MOZ_ASSERT_IF(!objLiteralData.empty(), alloc.contains(objLiteralData.data()));
3288 for (const auto& data : objLiteralData) {
3289 MOZ_ASSERT(data.isContainedIn(alloc));
3292 MOZ_ASSERT_IF(!parserAtomData.empty(), alloc.contains(parserAtomData.data()));
3293 for (const auto* data : parserAtomData) {
3294 MOZ_ASSERT_IF(data, alloc.contains(data));
3297 MOZ_ASSERT(!sharedData.isBorrow());
3300 void ExtensibleCompilationStencil::assertNoExternalDependency() const {
3301 for (const auto& data : bigIntData) {
3302 MOZ_ASSERT(data.isContainedIn(alloc));
3305 for (const auto& data : objLiteralData) {
3306 MOZ_ASSERT(data.isContainedIn(alloc));
3309 for (const auto* data : scopeNames) {
3310 MOZ_ASSERT_IF(data, alloc.contains(data));
3313 for (const auto* data : parserAtoms.entries()) {
3314 MOZ_ASSERT_IF(data, alloc.contains(data));
3317 MOZ_ASSERT(!sharedData.isBorrow());
3319 #endif // DEBUG
3321 template <typename T, typename VectorT>
3322 [[nodiscard]] bool CopySpanToVector(FrontendContext* fc, VectorT& vec,
3323 mozilla::Span<T>& span) {
3324 auto len = span.size();
3325 if (len == 0) {
3326 return true;
3329 if (!vec.append(span.data(), len)) {
3330 js::ReportOutOfMemory(fc);
3331 return false;
3333 return true;
3336 template <typename T, typename IntoSpanT, size_t Inline, typename AllocPolicy>
3337 [[nodiscard]] bool CopyToVector(FrontendContext* fc,
3338 mozilla::Vector<T, Inline, AllocPolicy>& vec,
3339 const IntoSpanT& source) {
3340 mozilla::Span<const T> span = source;
3341 return CopySpanToVector(fc, vec, span);
3344 // Span and Vector do not share the same method names.
3345 template <typename T, size_t Inline, typename AllocPolicy>
3346 size_t GetLength(const mozilla::Vector<T, Inline, AllocPolicy>& vec) {
3347 return vec.length();
3349 template <typename T>
3350 size_t GetLength(const mozilla::Span<T>& span) {
3351 return span.Length();
3354 // Copy scope names from `src` into `alloc`, and returns the allocated data.
3355 BaseParserScopeData* CopyScopeData(FrontendContext* fc, LifoAlloc& alloc,
3356 ScopeKind kind,
3357 const BaseParserScopeData* src) {
3358 MOZ_ASSERT(kind != ScopeKind::With);
3360 size_t dataSize = SizeOfParserScopeData(kind, src->length);
3362 auto* dest = static_cast<BaseParserScopeData*>(alloc.alloc(dataSize));
3363 if (!dest) {
3364 js::ReportOutOfMemory(fc);
3365 return nullptr;
3367 memcpy(dest, src, dataSize);
3369 return dest;
3372 template <typename Stencil>
3373 bool ExtensibleCompilationStencil::cloneFromImpl(FrontendContext* fc,
3374 const Stencil& other) {
3375 MOZ_ASSERT(alloc.isEmpty());
3377 canLazilyParse = other.canLazilyParse;
3378 functionKey = other.functionKey;
3380 if (!CopyToVector(fc, scriptData, other.scriptData)) {
3381 return false;
3384 if (!CopyToVector(fc, scriptExtra, other.scriptExtra)) {
3385 return false;
3388 if (!CopyToVector(fc, gcThingData, other.gcThingData)) {
3389 return false;
3392 size_t scopeSize = GetLength(other.scopeData);
3393 if (!CopyToVector(fc, scopeData, other.scopeData)) {
3394 return false;
3396 if (!scopeNames.reserve(scopeSize)) {
3397 js::ReportOutOfMemory(fc);
3398 return false;
3400 for (size_t i = 0; i < scopeSize; i++) {
3401 if (other.scopeNames[i]) {
3402 BaseParserScopeData* data = CopyScopeData(
3403 fc, alloc, other.scopeData[i].kind(), other.scopeNames[i]);
3404 if (!data) {
3405 return false;
3407 scopeNames.infallibleEmplaceBack(data);
3408 } else {
3409 scopeNames.infallibleEmplaceBack(nullptr);
3413 if (!CopyToVector(fc, regExpData, other.regExpData)) {
3414 return false;
3417 // If CompilationStencil has external dependency, peform deep copy.
3419 size_t bigIntSize = GetLength(other.bigIntData);
3420 if (!bigIntData.resize(bigIntSize)) {
3421 js::ReportOutOfMemory(fc);
3422 return false;
3424 for (size_t i = 0; i < bigIntSize; i++) {
3425 if (!bigIntData[i].init(fc, alloc, other.bigIntData[i].source())) {
3426 return false;
3430 size_t objLiteralSize = GetLength(other.objLiteralData);
3431 if (!objLiteralData.reserve(objLiteralSize)) {
3432 js::ReportOutOfMemory(fc);
3433 return false;
3435 for (const auto& data : other.objLiteralData) {
3436 size_t length = data.code().size();
3437 auto* code = alloc.newArrayUninitialized<uint8_t>(length);
3438 if (!code) {
3439 js::ReportOutOfMemory(fc);
3440 return false;
3442 memcpy(code, data.code().data(), length);
3443 objLiteralData.infallibleEmplaceBack(code, length, data.kind(),
3444 data.flags(), data.propertyCount());
3447 // Regardless of whether CompilationStencil has external dependency or not,
3448 // ParserAtoms should be interned, to populate internal HashMap.
3449 for (const auto* entry : other.parserAtomsSpan()) {
3450 if (!entry) {
3451 if (!parserAtoms.addPlaceholder(fc)) {
3452 return false;
3454 continue;
3457 auto index = parserAtoms.internExternalParserAtom(fc, entry);
3458 if (!index) {
3459 return false;
3463 // We copy the stencil and increment the reference count of each
3464 // SharedImmutableScriptData.
3465 if (!sharedData.cloneFrom(fc, other.sharedData)) {
3466 return false;
3469 // Note: moduleMetadata and asmJS are known after the first parse, and are
3470 // not mutated by any delazifications later on. Thus we can safely increment
3471 // the reference counter and keep these as-is.
3472 moduleMetadata = other.moduleMetadata;
3473 asmJS = other.asmJS;
3475 #ifdef DEBUG
3476 assertNoExternalDependency();
3477 #endif
3479 return true;
3482 bool ExtensibleCompilationStencil::cloneFrom(FrontendContext* fc,
3483 const CompilationStencil& other) {
3484 return cloneFromImpl(fc, other);
3486 bool ExtensibleCompilationStencil::cloneFrom(
3487 FrontendContext* fc, const ExtensibleCompilationStencil& other) {
3488 return cloneFromImpl(fc, other);
3491 bool ExtensibleCompilationStencil::steal(FrontendContext* fc,
3492 RefPtr<CompilationStencil>&& other) {
3493 MOZ_ASSERT(alloc.isEmpty());
3494 using StorageType = CompilationStencil::StorageType;
3495 StorageType storageType = other->storageType;
3496 if (other->refCount > 1) {
3497 storageType = StorageType::Borrowed;
3500 if (storageType == StorageType::OwnedExtensible) {
3501 auto& otherExtensible = other->ownedBorrowStencil;
3503 canLazilyParse = otherExtensible->canLazilyParse;
3504 functionKey = otherExtensible->functionKey;
3506 alloc.steal(&otherExtensible->alloc);
3508 source = std::move(otherExtensible->source);
3510 scriptData = std::move(otherExtensible->scriptData);
3511 scriptExtra = std::move(otherExtensible->scriptExtra);
3512 gcThingData = std::move(otherExtensible->gcThingData);
3513 scopeData = std::move(otherExtensible->scopeData);
3514 scopeNames = std::move(otherExtensible->scopeNames);
3515 regExpData = std::move(otherExtensible->regExpData);
3516 bigIntData = std::move(otherExtensible->bigIntData);
3517 objLiteralData = std::move(otherExtensible->objLiteralData);
3519 parserAtoms = std::move(otherExtensible->parserAtoms);
3520 parserAtoms.fixupAlloc(alloc);
3522 sharedData = std::move(otherExtensible->sharedData);
3523 moduleMetadata = std::move(otherExtensible->moduleMetadata);
3524 asmJS = std::move(otherExtensible->asmJS);
3526 #ifdef DEBUG
3527 assertNoExternalDependency();
3528 #endif
3530 return true;
3533 if (storageType == StorageType::Borrowed) {
3534 return cloneFrom(fc, *other);
3537 MOZ_ASSERT(storageType == StorageType::Owned);
3539 canLazilyParse = other->canLazilyParse;
3540 functionKey = other->functionKey;
3542 #ifdef DEBUG
3543 other->assertNoExternalDependency();
3544 MOZ_ASSERT(other->refCount == 1);
3545 #endif
3547 // If CompilationStencil has no external dependency,
3548 // steal LifoAlloc and perform shallow copy.
3549 alloc.steal(&other->alloc);
3551 if (!CopySpanToVector(fc, scriptData, other->scriptData)) {
3552 return false;
3555 if (!CopySpanToVector(fc, scriptExtra, other->scriptExtra)) {
3556 return false;
3559 if (!CopySpanToVector(fc, gcThingData, other->gcThingData)) {
3560 return false;
3563 if (!CopySpanToVector(fc, scopeData, other->scopeData)) {
3564 return false;
3566 if (!CopySpanToVector(fc, scopeNames, other->scopeNames)) {
3567 return false;
3570 if (!CopySpanToVector(fc, regExpData, other->regExpData)) {
3571 return false;
3574 if (!CopySpanToVector(fc, bigIntData, other->bigIntData)) {
3575 return false;
3578 if (!CopySpanToVector(fc, objLiteralData, other->objLiteralData)) {
3579 return false;
3582 // Regardless of whether CompilationStencil has external dependency or not,
3583 // ParserAtoms should be interned, to populate internal HashMap.
3584 for (const auto* entry : other->parserAtomData) {
3585 if (!entry) {
3586 if (!parserAtoms.addPlaceholder(fc)) {
3587 return false;
3589 continue;
3592 auto index = parserAtoms.internExternalParserAtom(fc, entry);
3593 if (!index) {
3594 return false;
3598 sharedData = std::move(other->sharedData);
3599 moduleMetadata = std::move(other->moduleMetadata);
3600 asmJS = std::move(other->asmJS);
3602 #ifdef DEBUG
3603 assertNoExternalDependency();
3604 #endif
3606 return true;
3609 bool CompilationStencil::isModule() const {
3610 return scriptExtra[CompilationStencil::TopLevelIndex].isModule();
3613 bool ExtensibleCompilationStencil::isModule() const {
3614 return scriptExtra[CompilationStencil::TopLevelIndex].isModule();
3617 mozilla::Span<TaggedScriptThingIndex> ScriptStencil::gcthings(
3618 const CompilationStencil& stencil) const {
3619 return stencil.gcThingData.Subspan(gcThingsOffset, gcThingsLength);
3622 bool BigIntStencil::init(FrontendContext* fc, LifoAlloc& alloc,
3623 const mozilla::Span<const char16_t> buf) {
3624 #ifdef DEBUG
3625 // Assert we have no separators; if we have a separator then the algorithm
3626 // used in BigInt::literalIsZero will be incorrect.
3627 for (char16_t c : buf) {
3628 MOZ_ASSERT(c != '_');
3630 #endif
3631 size_t length = buf.size();
3632 char16_t* p = alloc.template newArrayUninitialized<char16_t>(length);
3633 if (!p) {
3634 ReportOutOfMemory(fc);
3635 return false;
3637 mozilla::PodCopy(p, buf.data(), length);
3638 source_ = mozilla::Span(p, length);
3639 return true;
3642 BigInt* BigIntStencil::createBigInt(JSContext* cx) const {
3643 mozilla::Range<const char16_t> source(source_.data(), source_.size());
3644 return js::ParseBigIntLiteral(cx, source);
3647 bool BigIntStencil::isZero() const {
3648 mozilla::Range<const char16_t> source(source_.data(), source_.size());
3649 return js::BigIntLiteralIsZero(source);
3652 #ifdef DEBUG
3653 bool BigIntStencil::isContainedIn(const LifoAlloc& alloc) const {
3654 return alloc.contains(source_.data());
3656 #endif
3658 #if defined(DEBUG) || defined(JS_JITSPEW)
3660 void frontend::DumpTaggedParserAtomIndex(js::JSONPrinter& json,
3661 TaggedParserAtomIndex taggedIndex,
3662 const CompilationStencil* stencil) {
3663 if (taggedIndex.isParserAtomIndex()) {
3664 json.property("tag", "AtomIndex");
3665 auto index = taggedIndex.toParserAtomIndex();
3666 if (stencil && stencil->parserAtomData[index]) {
3667 GenericPrinter& out = json.beginStringProperty("atom");
3668 stencil->parserAtomData[index]->dumpCharsNoQuote(out);
3669 json.endString();
3670 } else {
3671 json.property("index", size_t(index));
3673 return;
3676 if (taggedIndex.isWellKnownAtomId()) {
3677 json.property("tag", "WellKnown");
3678 auto index = taggedIndex.toWellKnownAtomId();
3679 switch (index) {
3680 case WellKnownAtomId::empty_:
3681 json.property("atom", "");
3682 break;
3684 # define CASE_(name, _) case WellKnownAtomId::name:
3685 FOR_EACH_NONTINY_COMMON_PROPERTYNAME(CASE_)
3686 # undef CASE_
3688 # define CASE_(name, _) case WellKnownAtomId::name:
3689 JS_FOR_EACH_PROTOTYPE(CASE_)
3690 # undef CASE_
3692 # define CASE_(name) case WellKnownAtomId::name:
3693 JS_FOR_EACH_WELL_KNOWN_SYMBOL(CASE_)
3694 # undef CASE_
3697 GenericPrinter& out = json.beginStringProperty("atom");
3698 ParserAtomsTable::dumpCharsNoQuote(out, index);
3699 json.endString();
3700 break;
3703 default:
3704 // This includes tiny WellKnownAtomId atoms, which is invalid.
3705 json.property("index", size_t(index));
3706 break;
3708 return;
3711 if (taggedIndex.isLength1StaticParserString()) {
3712 json.property("tag", "Length1Static");
3713 auto index = taggedIndex.toLength1StaticParserString();
3714 GenericPrinter& out = json.beginStringProperty("atom");
3715 ParserAtomsTable::dumpCharsNoQuote(out, index);
3716 json.endString();
3717 return;
3720 if (taggedIndex.isLength2StaticParserString()) {
3721 json.property("tag", "Length2Static");
3722 auto index = taggedIndex.toLength2StaticParserString();
3723 GenericPrinter& out = json.beginStringProperty("atom");
3724 ParserAtomsTable::dumpCharsNoQuote(out, index);
3725 json.endString();
3726 return;
3729 if (taggedIndex.isLength3StaticParserString()) {
3730 json.property("tag", "Length3Static");
3731 auto index = taggedIndex.toLength3StaticParserString();
3732 GenericPrinter& out = json.beginStringProperty("atom");
3733 ParserAtomsTable::dumpCharsNoQuote(out, index);
3734 json.endString();
3735 return;
3738 MOZ_ASSERT(taggedIndex.isNull());
3739 json.property("tag", "null");
3742 void frontend::DumpTaggedParserAtomIndexNoQuote(
3743 GenericPrinter& out, TaggedParserAtomIndex taggedIndex,
3744 const CompilationStencil* stencil) {
3745 if (taggedIndex.isParserAtomIndex()) {
3746 auto index = taggedIndex.toParserAtomIndex();
3747 if (stencil && stencil->parserAtomData[index]) {
3748 stencil->parserAtomData[index]->dumpCharsNoQuote(out);
3749 } else {
3750 out.printf("AtomIndex#%zu", size_t(index));
3752 return;
3755 if (taggedIndex.isWellKnownAtomId()) {
3756 auto index = taggedIndex.toWellKnownAtomId();
3757 switch (index) {
3758 case WellKnownAtomId::empty_:
3759 out.put("#<zero-length name>");
3760 break;
3762 # define CASE_(name, _) case WellKnownAtomId::name:
3763 FOR_EACH_NONTINY_COMMON_PROPERTYNAME(CASE_)
3764 # undef CASE_
3766 # define CASE_(name, _) case WellKnownAtomId::name:
3767 JS_FOR_EACH_PROTOTYPE(CASE_)
3768 # undef CASE_
3770 # define CASE_(name) case WellKnownAtomId::name:
3771 JS_FOR_EACH_WELL_KNOWN_SYMBOL(CASE_)
3772 # undef CASE_
3775 ParserAtomsTable::dumpCharsNoQuote(out, index);
3776 break;
3779 default:
3780 // This includes tiny WellKnownAtomId atoms, which is invalid.
3781 out.printf("WellKnown#%zu", size_t(index));
3782 break;
3784 return;
3787 if (taggedIndex.isLength1StaticParserString()) {
3788 auto index = taggedIndex.toLength1StaticParserString();
3789 ParserAtomsTable::dumpCharsNoQuote(out, index);
3790 return;
3793 if (taggedIndex.isLength2StaticParserString()) {
3794 auto index = taggedIndex.toLength2StaticParserString();
3795 ParserAtomsTable::dumpCharsNoQuote(out, index);
3796 return;
3799 if (taggedIndex.isLength3StaticParserString()) {
3800 auto index = taggedIndex.toLength3StaticParserString();
3801 ParserAtomsTable::dumpCharsNoQuote(out, index);
3802 return;
3805 MOZ_ASSERT(taggedIndex.isNull());
3806 out.put("#<null name>");
3809 void RegExpStencil::dump() const {
3810 js::Fprinter out(stderr);
3811 js::JSONPrinter json(out);
3812 dump(json, nullptr);
3815 void RegExpStencil::dump(js::JSONPrinter& json,
3816 const CompilationStencil* stencil) const {
3817 json.beginObject();
3818 dumpFields(json, stencil);
3819 json.endObject();
3822 void RegExpStencil::dumpFields(js::JSONPrinter& json,
3823 const CompilationStencil* stencil) const {
3824 json.beginObjectProperty("pattern");
3825 DumpTaggedParserAtomIndex(json, atom_, stencil);
3826 json.endObject();
3828 GenericPrinter& out = json.beginStringProperty("flags");
3830 if (flags().global()) {
3831 out.put("g");
3833 if (flags().ignoreCase()) {
3834 out.put("i");
3836 if (flags().multiline()) {
3837 out.put("m");
3839 if (flags().dotAll()) {
3840 out.put("s");
3842 if (flags().unicode()) {
3843 out.put("u");
3845 if (flags().sticky()) {
3846 out.put("y");
3849 json.endStringProperty();
3852 void BigIntStencil::dump() const {
3853 js::Fprinter out(stderr);
3854 js::JSONPrinter json(out);
3855 dump(json);
3858 void BigIntStencil::dump(js::JSONPrinter& json) const {
3859 GenericPrinter& out = json.beginString();
3860 dumpCharsNoQuote(out);
3861 json.endString();
3864 void BigIntStencil::dumpCharsNoQuote(GenericPrinter& out) const {
3865 for (char16_t c : source_) {
3866 out.putChar(char(c));
3870 void ScopeStencil::dump() const {
3871 js::Fprinter out(stderr);
3872 js::JSONPrinter json(out);
3873 dump(json, nullptr, nullptr);
3876 void ScopeStencil::dump(js::JSONPrinter& json,
3877 const BaseParserScopeData* baseScopeData,
3878 const CompilationStencil* stencil) const {
3879 json.beginObject();
3880 dumpFields(json, baseScopeData, stencil);
3881 json.endObject();
3884 void ScopeStencil::dumpFields(js::JSONPrinter& json,
3885 const BaseParserScopeData* baseScopeData,
3886 const CompilationStencil* stencil) const {
3887 json.property("kind", ScopeKindString(kind_));
3889 if (hasEnclosing()) {
3890 json.formatProperty("enclosing", "ScopeIndex(%zu)", size_t(enclosing()));
3893 json.property("firstFrameSlot", firstFrameSlot_);
3895 if (hasEnvironmentShape()) {
3896 json.formatProperty("numEnvironmentSlots", "%zu",
3897 size_t(numEnvironmentSlots_));
3900 if (isFunction()) {
3901 json.formatProperty("functionIndex", "ScriptIndex(%zu)",
3902 size_t(functionIndex_));
3905 json.beginListProperty("flags");
3906 if (flags_ & HasEnclosing) {
3907 json.value("HasEnclosing");
3909 if (flags_ & HasEnvironmentShape) {
3910 json.value("HasEnvironmentShape");
3912 if (flags_ & IsArrow) {
3913 json.value("IsArrow");
3915 json.endList();
3917 if (!baseScopeData) {
3918 return;
3921 json.beginObjectProperty("data");
3923 mozilla::Span<const ParserBindingName> trailingNames;
3924 switch (kind_) {
3925 case ScopeKind::Function: {
3926 const auto* data =
3927 static_cast<const FunctionScope::ParserData*>(baseScopeData);
3928 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3929 json.property("hasParameterExprs", data->slotInfo.hasParameterExprs());
3930 json.property("nonPositionalFormalStart",
3931 data->slotInfo.nonPositionalFormalStart);
3932 json.property("varStart", data->slotInfo.varStart);
3934 trailingNames = GetScopeDataTrailingNames(data);
3935 break;
3938 case ScopeKind::FunctionBodyVar: {
3939 const auto* data =
3940 static_cast<const VarScope::ParserData*>(baseScopeData);
3941 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3943 trailingNames = GetScopeDataTrailingNames(data);
3944 break;
3947 case ScopeKind::Lexical:
3948 case ScopeKind::SimpleCatch:
3949 case ScopeKind::Catch:
3950 case ScopeKind::NamedLambda:
3951 case ScopeKind::StrictNamedLambda:
3952 case ScopeKind::FunctionLexical: {
3953 const auto* data =
3954 static_cast<const LexicalScope::ParserData*>(baseScopeData);
3955 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3956 json.property("constStart", data->slotInfo.constStart);
3958 trailingNames = GetScopeDataTrailingNames(data);
3959 break;
3962 case ScopeKind::ClassBody: {
3963 const auto* data =
3964 static_cast<const ClassBodyScope::ParserData*>(baseScopeData);
3965 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3966 json.property("privateMethodStart", data->slotInfo.privateMethodStart);
3968 trailingNames = GetScopeDataTrailingNames(data);
3969 break;
3972 case ScopeKind::With: {
3973 break;
3976 case ScopeKind::Eval:
3977 case ScopeKind::StrictEval: {
3978 const auto* data =
3979 static_cast<const EvalScope::ParserData*>(baseScopeData);
3980 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3982 trailingNames = GetScopeDataTrailingNames(data);
3983 break;
3986 case ScopeKind::Global:
3987 case ScopeKind::NonSyntactic: {
3988 const auto* data =
3989 static_cast<const GlobalScope::ParserData*>(baseScopeData);
3990 json.property("letStart", data->slotInfo.letStart);
3991 json.property("constStart", data->slotInfo.constStart);
3993 trailingNames = GetScopeDataTrailingNames(data);
3994 break;
3997 case ScopeKind::Module: {
3998 const auto* data =
3999 static_cast<const ModuleScope::ParserData*>(baseScopeData);
4000 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
4001 json.property("varStart", data->slotInfo.varStart);
4002 json.property("letStart", data->slotInfo.letStart);
4003 json.property("constStart", data->slotInfo.constStart);
4005 trailingNames = GetScopeDataTrailingNames(data);
4006 break;
4009 case ScopeKind::WasmInstance: {
4010 const auto* data =
4011 static_cast<const WasmInstanceScope::ParserData*>(baseScopeData);
4012 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
4013 json.property("globalsStart", data->slotInfo.globalsStart);
4015 trailingNames = GetScopeDataTrailingNames(data);
4016 break;
4019 case ScopeKind::WasmFunction: {
4020 const auto* data =
4021 static_cast<const WasmFunctionScope::ParserData*>(baseScopeData);
4022 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
4024 trailingNames = GetScopeDataTrailingNames(data);
4025 break;
4028 default: {
4029 MOZ_CRASH("Unexpected ScopeKind");
4030 break;
4034 if (!trailingNames.empty()) {
4035 char index[64];
4036 json.beginObjectProperty("trailingNames");
4037 for (size_t i = 0; i < trailingNames.size(); i++) {
4038 const auto& name = trailingNames[i];
4039 SprintfLiteral(index, "%zu", i);
4040 json.beginObjectProperty(index);
4042 json.boolProperty("closedOver", name.closedOver());
4044 json.boolProperty("isTopLevelFunction", name.isTopLevelFunction());
4046 json.beginObjectProperty("name");
4047 DumpTaggedParserAtomIndex(json, name.name(), stencil);
4048 json.endObject();
4050 json.endObject();
4052 json.endObject();
4055 json.endObject();
4058 static void DumpModuleRequestVectorItems(
4059 js::JSONPrinter& json, const StencilModuleMetadata::RequestVector& requests,
4060 const CompilationStencil* stencil) {
4061 for (const auto& request : requests) {
4062 json.beginObject();
4063 if (request.specifier) {
4064 json.beginObjectProperty("specifier");
4065 DumpTaggedParserAtomIndex(json, request.specifier, stencil);
4066 json.endObject();
4068 json.endObject();
4072 static void DumpModuleEntryVectorItems(
4073 js::JSONPrinter& json, const StencilModuleMetadata::EntryVector& entries,
4074 const CompilationStencil* stencil) {
4075 for (const auto& entry : entries) {
4076 json.beginObject();
4077 if (entry.moduleRequest) {
4078 json.property("moduleRequest", entry.moduleRequest.value());
4080 if (entry.localName) {
4081 json.beginObjectProperty("localName");
4082 DumpTaggedParserAtomIndex(json, entry.localName, stencil);
4083 json.endObject();
4085 if (entry.importName) {
4086 json.beginObjectProperty("importName");
4087 DumpTaggedParserAtomIndex(json, entry.importName, stencil);
4088 json.endObject();
4090 if (entry.exportName) {
4091 json.beginObjectProperty("exportName");
4092 DumpTaggedParserAtomIndex(json, entry.exportName, stencil);
4093 json.endObject();
4095 // TODO: Dump assertions.
4096 json.endObject();
4100 void StencilModuleMetadata::dump() const {
4101 js::Fprinter out(stderr);
4102 js::JSONPrinter json(out);
4103 dump(json, nullptr);
4106 void StencilModuleMetadata::dump(js::JSONPrinter& json,
4107 const CompilationStencil* stencil) const {
4108 json.beginObject();
4109 dumpFields(json, stencil);
4110 json.endObject();
4113 void StencilModuleMetadata::dumpFields(
4114 js::JSONPrinter& json, const CompilationStencil* stencil) const {
4115 json.beginListProperty("moduleRequests");
4116 DumpModuleRequestVectorItems(json, moduleRequests, stencil);
4117 json.endList();
4119 json.beginListProperty("requestedModules");
4120 DumpModuleEntryVectorItems(json, requestedModules, stencil);
4121 json.endList();
4123 json.beginListProperty("importEntries");
4124 DumpModuleEntryVectorItems(json, importEntries, stencil);
4125 json.endList();
4127 json.beginListProperty("localExportEntries");
4128 DumpModuleEntryVectorItems(json, localExportEntries, stencil);
4129 json.endList();
4131 json.beginListProperty("indirectExportEntries");
4132 DumpModuleEntryVectorItems(json, indirectExportEntries, stencil);
4133 json.endList();
4135 json.beginListProperty("starExportEntries");
4136 DumpModuleEntryVectorItems(json, starExportEntries, stencil);
4137 json.endList();
4139 json.beginListProperty("functionDecls");
4140 for (const auto& index : functionDecls) {
4141 json.value("ScriptIndex(%zu)", size_t(index));
4143 json.endList();
4145 json.boolProperty("isAsync", isAsync);
4148 void js::DumpImmutableScriptFlags(js::JSONPrinter& json,
4149 ImmutableScriptFlags immutableFlags) {
4150 for (uint32_t i = 1; i; i = i << 1) {
4151 if (uint32_t(immutableFlags) & i) {
4152 switch (ImmutableScriptFlagsEnum(i)) {
4153 case ImmutableScriptFlagsEnum::IsForEval:
4154 json.value("IsForEval");
4155 break;
4156 case ImmutableScriptFlagsEnum::IsModule:
4157 json.value("IsModule");
4158 break;
4159 case ImmutableScriptFlagsEnum::IsFunction:
4160 json.value("IsFunction");
4161 break;
4162 case ImmutableScriptFlagsEnum::SelfHosted:
4163 json.value("SelfHosted");
4164 break;
4165 case ImmutableScriptFlagsEnum::ForceStrict:
4166 json.value("ForceStrict");
4167 break;
4168 case ImmutableScriptFlagsEnum::HasNonSyntacticScope:
4169 json.value("HasNonSyntacticScope");
4170 break;
4171 case ImmutableScriptFlagsEnum::NoScriptRval:
4172 json.value("NoScriptRval");
4173 break;
4174 case ImmutableScriptFlagsEnum::TreatAsRunOnce:
4175 json.value("TreatAsRunOnce");
4176 break;
4177 case ImmutableScriptFlagsEnum::Strict:
4178 json.value("Strict");
4179 break;
4180 case ImmutableScriptFlagsEnum::HasModuleGoal:
4181 json.value("HasModuleGoal");
4182 break;
4183 case ImmutableScriptFlagsEnum::HasInnerFunctions:
4184 json.value("HasInnerFunctions");
4185 break;
4186 case ImmutableScriptFlagsEnum::HasDirectEval:
4187 json.value("HasDirectEval");
4188 break;
4189 case ImmutableScriptFlagsEnum::BindingsAccessedDynamically:
4190 json.value("BindingsAccessedDynamically");
4191 break;
4192 case ImmutableScriptFlagsEnum::HasCallSiteObj:
4193 json.value("HasCallSiteObj");
4194 break;
4195 case ImmutableScriptFlagsEnum::IsAsync:
4196 json.value("IsAsync");
4197 break;
4198 case ImmutableScriptFlagsEnum::IsGenerator:
4199 json.value("IsGenerator");
4200 break;
4201 case ImmutableScriptFlagsEnum::FunHasExtensibleScope:
4202 json.value("FunHasExtensibleScope");
4203 break;
4204 case ImmutableScriptFlagsEnum::FunctionHasThisBinding:
4205 json.value("FunctionHasThisBinding");
4206 break;
4207 case ImmutableScriptFlagsEnum::NeedsHomeObject:
4208 json.value("NeedsHomeObject");
4209 break;
4210 case ImmutableScriptFlagsEnum::IsDerivedClassConstructor:
4211 json.value("IsDerivedClassConstructor");
4212 break;
4213 case ImmutableScriptFlagsEnum::IsSyntheticFunction:
4214 json.value("IsSyntheticFunction");
4215 break;
4216 case ImmutableScriptFlagsEnum::UseMemberInitializers:
4217 json.value("UseMemberInitializers");
4218 break;
4219 case ImmutableScriptFlagsEnum::HasRest:
4220 json.value("HasRest");
4221 break;
4222 case ImmutableScriptFlagsEnum::NeedsFunctionEnvironmentObjects:
4223 json.value("NeedsFunctionEnvironmentObjects");
4224 break;
4225 case ImmutableScriptFlagsEnum::FunctionHasExtraBodyVarScope:
4226 json.value("FunctionHasExtraBodyVarScope");
4227 break;
4228 case ImmutableScriptFlagsEnum::ShouldDeclareArguments:
4229 json.value("ShouldDeclareArguments");
4230 break;
4231 case ImmutableScriptFlagsEnum::NeedsArgsObj:
4232 json.value("NeedsArgsObj");
4233 break;
4234 case ImmutableScriptFlagsEnum::HasMappedArgsObj:
4235 json.value("HasMappedArgsObj");
4236 break;
4237 case ImmutableScriptFlagsEnum::IsInlinableLargeFunction:
4238 json.value("IsInlinableLargeFunction");
4239 break;
4240 case ImmutableScriptFlagsEnum::FunctionHasNewTargetBinding:
4241 json.value("FunctionHasNewTargetBinding");
4242 break;
4243 case ImmutableScriptFlagsEnum::UsesArgumentsIntrinsics:
4244 json.value("UsesArgumentsIntrinsics");
4245 break;
4246 default:
4247 json.value("Unknown(%x)", i);
4248 break;
4254 void js::DumpFunctionFlagsItems(js::JSONPrinter& json,
4255 FunctionFlags functionFlags) {
4256 switch (functionFlags.kind()) {
4257 case FunctionFlags::FunctionKind::NormalFunction:
4258 json.value("NORMAL_KIND");
4259 break;
4260 case FunctionFlags::FunctionKind::AsmJS:
4261 json.value("ASMJS_KIND");
4262 break;
4263 case FunctionFlags::FunctionKind::Wasm:
4264 json.value("WASM_KIND");
4265 break;
4266 case FunctionFlags::FunctionKind::Arrow:
4267 json.value("ARROW_KIND");
4268 break;
4269 case FunctionFlags::FunctionKind::Method:
4270 json.value("METHOD_KIND");
4271 break;
4272 case FunctionFlags::FunctionKind::ClassConstructor:
4273 json.value("CLASSCONSTRUCTOR_KIND");
4274 break;
4275 case FunctionFlags::FunctionKind::Getter:
4276 json.value("GETTER_KIND");
4277 break;
4278 case FunctionFlags::FunctionKind::Setter:
4279 json.value("SETTER_KIND");
4280 break;
4281 default:
4282 json.value("Unknown(%x)", uint8_t(functionFlags.kind()));
4283 break;
4286 static_assert(FunctionFlags::FUNCTION_KIND_MASK == 0x0007,
4287 "FunctionKind should use the lowest 3 bits");
4288 for (uint16_t i = 1 << 3; i; i = i << 1) {
4289 if (functionFlags.toRaw() & i) {
4290 switch (FunctionFlags::Flags(i)) {
4291 case FunctionFlags::Flags::EXTENDED:
4292 json.value("EXTENDED");
4293 break;
4294 case FunctionFlags::Flags::SELF_HOSTED:
4295 json.value("SELF_HOSTED");
4296 break;
4297 case FunctionFlags::Flags::BASESCRIPT:
4298 json.value("BASESCRIPT");
4299 break;
4300 case FunctionFlags::Flags::SELFHOSTLAZY:
4301 json.value("SELFHOSTLAZY");
4302 break;
4303 case FunctionFlags::Flags::CONSTRUCTOR:
4304 json.value("CONSTRUCTOR");
4305 break;
4306 case FunctionFlags::Flags::LAZY_ACCESSOR_NAME:
4307 json.value("LAZY_ACCESSOR_NAME");
4308 break;
4309 case FunctionFlags::Flags::LAMBDA:
4310 json.value("LAMBDA");
4311 break;
4312 case FunctionFlags::Flags::NATIVE_JIT_ENTRY:
4313 json.value("NATIVE_JIT_ENTRY");
4314 break;
4315 case FunctionFlags::Flags::HAS_INFERRED_NAME:
4316 json.value("HAS_INFERRED_NAME");
4317 break;
4318 case FunctionFlags::Flags::HAS_GUESSED_ATOM:
4319 json.value("HAS_GUESSED_ATOM");
4320 break;
4321 case FunctionFlags::Flags::RESOLVED_NAME:
4322 json.value("RESOLVED_NAME");
4323 break;
4324 case FunctionFlags::Flags::RESOLVED_LENGTH:
4325 json.value("RESOLVED_LENGTH");
4326 break;
4327 case FunctionFlags::Flags::GHOST_FUNCTION:
4328 json.value("GHOST_FUNCTION");
4329 break;
4330 default:
4331 json.value("Unknown(%x)", i);
4332 break;
4338 static void DumpScriptThing(js::JSONPrinter& json,
4339 const CompilationStencil* stencil,
4340 TaggedScriptThingIndex thing) {
4341 switch (thing.tag()) {
4342 case TaggedScriptThingIndex::Kind::ParserAtomIndex:
4343 case TaggedScriptThingIndex::Kind::WellKnown:
4344 json.beginObject();
4345 json.property("type", "Atom");
4346 DumpTaggedParserAtomIndex(json, thing.toAtom(), stencil);
4347 json.endObject();
4348 break;
4349 case TaggedScriptThingIndex::Kind::Null:
4350 json.nullValue();
4351 break;
4352 case TaggedScriptThingIndex::Kind::BigInt:
4353 json.value("BigIntIndex(%zu)", size_t(thing.toBigInt()));
4354 break;
4355 case TaggedScriptThingIndex::Kind::ObjLiteral:
4356 json.value("ObjLiteralIndex(%zu)", size_t(thing.toObjLiteral()));
4357 break;
4358 case TaggedScriptThingIndex::Kind::RegExp:
4359 json.value("RegExpIndex(%zu)", size_t(thing.toRegExp()));
4360 break;
4361 case TaggedScriptThingIndex::Kind::Scope:
4362 json.value("ScopeIndex(%zu)", size_t(thing.toScope()));
4363 break;
4364 case TaggedScriptThingIndex::Kind::Function:
4365 json.value("ScriptIndex(%zu)", size_t(thing.toFunction()));
4366 break;
4367 case TaggedScriptThingIndex::Kind::EmptyGlobalScope:
4368 json.value("EmptyGlobalScope");
4369 break;
4373 void ScriptStencil::dump() const {
4374 js::Fprinter out(stderr);
4375 js::JSONPrinter json(out);
4376 dump(json, nullptr);
4379 void ScriptStencil::dump(js::JSONPrinter& json,
4380 const CompilationStencil* stencil) const {
4381 json.beginObject();
4382 dumpFields(json, stencil);
4383 json.endObject();
4386 void ScriptStencil::dumpFields(js::JSONPrinter& json,
4387 const CompilationStencil* stencil) const {
4388 json.formatProperty("gcThingsOffset", "CompilationGCThingIndex(%u)",
4389 gcThingsOffset.index);
4390 json.property("gcThingsLength", gcThingsLength);
4392 if (stencil) {
4393 json.beginListProperty("gcThings");
4394 for (const auto& thing : gcthings(*stencil)) {
4395 DumpScriptThing(json, stencil, thing);
4397 json.endList();
4400 json.beginListProperty("flags");
4401 if (flags_ & WasEmittedByEnclosingScriptFlag) {
4402 json.value("WasEmittedByEnclosingScriptFlag");
4404 if (flags_ & AllowRelazifyFlag) {
4405 json.value("AllowRelazifyFlag");
4407 if (flags_ & HasSharedDataFlag) {
4408 json.value("HasSharedDataFlag");
4410 if (flags_ & HasLazyFunctionEnclosingScopeIndexFlag) {
4411 json.value("HasLazyFunctionEnclosingScopeIndexFlag");
4413 json.endList();
4415 if (isFunction()) {
4416 json.beginObjectProperty("functionAtom");
4417 DumpTaggedParserAtomIndex(json, functionAtom, stencil);
4418 json.endObject();
4420 json.beginListProperty("functionFlags");
4421 DumpFunctionFlagsItems(json, functionFlags);
4422 json.endList();
4424 if (hasLazyFunctionEnclosingScopeIndex()) {
4425 json.formatProperty("lazyFunctionEnclosingScopeIndex", "ScopeIndex(%zu)",
4426 size_t(lazyFunctionEnclosingScopeIndex()));
4429 if (hasSelfHostedCanonicalName()) {
4430 json.beginObjectProperty("selfHostCanonicalName");
4431 DumpTaggedParserAtomIndex(json, selfHostedCanonicalName(), stencil);
4432 json.endObject();
4437 void ScriptStencilExtra::dump() const {
4438 js::Fprinter out(stderr);
4439 js::JSONPrinter json(out);
4440 dump(json);
4443 void ScriptStencilExtra::dump(js::JSONPrinter& json) const {
4444 json.beginObject();
4445 dumpFields(json);
4446 json.endObject();
4449 void ScriptStencilExtra::dumpFields(js::JSONPrinter& json) const {
4450 json.beginListProperty("immutableFlags");
4451 DumpImmutableScriptFlags(json, immutableFlags);
4452 json.endList();
4454 json.beginObjectProperty("extent");
4455 json.property("sourceStart", extent.sourceStart);
4456 json.property("sourceEnd", extent.sourceEnd);
4457 json.property("toStringStart", extent.toStringStart);
4458 json.property("toStringEnd", extent.toStringEnd);
4459 json.property("lineno", extent.lineno);
4460 json.property("column", extent.column.oneOriginValue());
4461 json.endObject();
4463 json.property("memberInitializers", memberInitializers_);
4465 json.property("nargs", nargs);
4468 void SharedDataContainer::dump() const {
4469 js::Fprinter out(stderr);
4470 js::JSONPrinter json(out);
4471 dump(json);
4474 void SharedDataContainer::dump(js::JSONPrinter& json) const {
4475 json.beginObject();
4476 dumpFields(json);
4477 json.endObject();
4480 void SharedDataContainer::dumpFields(js::JSONPrinter& json) const {
4481 if (isEmpty()) {
4482 json.nullProperty("ScriptIndex(0)");
4483 return;
4486 if (isSingle()) {
4487 json.formatProperty("ScriptIndex(0)", "u8[%zu]",
4488 asSingle()->immutableDataLength());
4489 return;
4492 if (isVector()) {
4493 auto& vec = *asVector();
4495 char index[64];
4496 for (size_t i = 0; i < vec.length(); i++) {
4497 SprintfLiteral(index, "ScriptIndex(%zu)", i);
4498 if (vec[i]) {
4499 json.formatProperty(index, "u8[%zu]", vec[i]->immutableDataLength());
4500 } else {
4501 json.nullProperty(index);
4504 return;
4507 if (isMap()) {
4508 auto& map = *asMap();
4510 char index[64];
4511 for (auto iter = map.iter(); !iter.done(); iter.next()) {
4512 SprintfLiteral(index, "ScriptIndex(%u)", iter.get().key().index);
4513 json.formatProperty(index, "u8[%zu]",
4514 iter.get().value()->immutableDataLength());
4516 return;
4519 MOZ_ASSERT(isBorrow());
4520 asBorrow()->dumpFields(json);
4523 struct DumpOptionsFields {
4524 js::JSONPrinter& json;
4526 void operator()(const char* name, JS::AsmJSOption value) {
4527 const char* valueStr = nullptr;
4528 switch (value) {
4529 case JS::AsmJSOption::Enabled:
4530 valueStr = "JS::AsmJSOption::Enabled";
4531 break;
4532 case JS::AsmJSOption::DisabledByAsmJSPref:
4533 valueStr = "JS::AsmJSOption::DisabledByAsmJSPref";
4534 break;
4535 case JS::AsmJSOption::DisabledByLinker:
4536 valueStr = "JS::AsmJSOption::DisabledByLinker";
4537 break;
4538 case JS::AsmJSOption::DisabledByNoWasmCompiler:
4539 valueStr = "JS::AsmJSOption::DisabledByNoWasmCompiler";
4540 break;
4541 case JS::AsmJSOption::DisabledByDebugger:
4542 valueStr = "JS::AsmJSOption::DisabledByDebugger";
4543 break;
4545 json.property(name, valueStr);
4548 void operator()(const char* name, JS::DelazificationOption value) {
4549 const char* valueStr = nullptr;
4550 switch (value) {
4551 # define SelectValueStr_(Strategy) \
4552 case JS::DelazificationOption::Strategy: \
4553 valueStr = "JS::DelazificationOption::" #Strategy; \
4554 break;
4556 FOREACH_DELAZIFICATION_STRATEGY(SelectValueStr_)
4557 # undef SelectValueStr_
4559 json.property(name, valueStr);
4562 void operator()(const char* name, char16_t* value) {}
4564 void operator()(const char* name, bool value) { json.property(name, value); }
4566 void operator()(const char* name, uint32_t value) {
4567 json.property(name, value);
4570 void operator()(const char* name, uint64_t value) {
4571 json.property(name, value);
4574 void operator()(const char* name, const char* value) {
4575 if (value) {
4576 json.property(name, value);
4577 return;
4579 json.nullProperty(name);
4582 void operator()(const char* name, JS::ConstUTF8CharsZ value) {
4583 if (value) {
4584 json.property(name, value.c_str());
4585 return;
4587 json.nullProperty(name);
4591 static void DumpOptionsFields(js::JSONPrinter& json,
4592 const JS::ReadOnlyCompileOptions& options) {
4593 struct DumpOptionsFields printer {
4594 json
4596 options.dumpWith(printer);
4599 static void DumpInputScopeFields(js::JSONPrinter& json,
4600 const InputScope& scope) {
4601 json.property("kind", ScopeKindString(scope.kind()));
4603 InputScope enclosing = scope.enclosing();
4604 if (enclosing.isNull()) {
4605 json.nullProperty("enclosing");
4606 } else {
4607 json.beginObjectProperty("enclosing");
4608 DumpInputScopeFields(json, enclosing);
4609 json.endObject();
4613 static void DumpInputScriptFields(js::JSONPrinter& json,
4614 const InputScript& script) {
4615 json.beginObjectProperty("extent");
4617 SourceExtent extent = script.extent();
4618 json.property("sourceStart", extent.sourceStart);
4619 json.property("sourceEnd", extent.sourceEnd);
4620 json.property("toStringStart", extent.toStringStart);
4621 json.property("toStringEnd", extent.toStringEnd);
4622 json.property("lineno", extent.lineno);
4623 json.property("column", extent.column.oneOriginValue());
4625 json.endObject();
4627 json.beginListProperty("immutableFlags");
4628 DumpImmutableScriptFlags(json, script.immutableFlags());
4629 json.endList();
4631 json.beginListProperty("functionFlags");
4632 DumpFunctionFlagsItems(json, script.functionFlags());
4633 json.endList();
4635 json.property("hasPrivateScriptData", script.hasPrivateScriptData());
4637 InputScope scope = script.enclosingScope();
4638 if (scope.isNull()) {
4639 json.nullProperty("enclosingScope");
4640 } else {
4641 json.beginObjectProperty("enclosingScope");
4642 DumpInputScopeFields(json, scope);
4643 json.endObject();
4646 if (script.useMemberInitializers()) {
4647 json.property("memberInitializers",
4648 script.getMemberInitializers().serialize());
4652 void CompilationInput::dump() const {
4653 js::Fprinter out(stderr);
4654 js::JSONPrinter json(out);
4655 dump(json);
4656 out.put("\n");
4659 void CompilationInput::dump(js::JSONPrinter& json) const {
4660 json.beginObject();
4661 dumpFields(json);
4662 json.endObject();
4665 void CompilationInput::dumpFields(js::JSONPrinter& json) const {
4666 const char* targetStr = nullptr;
4667 switch (target) {
4668 case CompilationTarget::Global:
4669 targetStr = "CompilationTarget::Global";
4670 break;
4671 case CompilationTarget::SelfHosting:
4672 targetStr = "CompilationTarget::SelfHosting";
4673 break;
4674 case CompilationTarget::StandaloneFunction:
4675 targetStr = "CompilationTarget::StandaloneFunction";
4676 break;
4677 case CompilationTarget::StandaloneFunctionInNonSyntacticScope:
4678 targetStr = "CompilationTarget::StandaloneFunctionInNonSyntacticScope";
4679 break;
4680 case CompilationTarget::Eval:
4681 targetStr = "CompilationTarget::Eval";
4682 break;
4683 case CompilationTarget::Module:
4684 targetStr = "CompilationTarget::Module";
4685 break;
4686 case CompilationTarget::Delazification:
4687 targetStr = "CompilationTarget::Delazification";
4688 break;
4690 json.property("target", targetStr);
4692 json.beginObjectProperty("options");
4693 DumpOptionsFields(json, options);
4694 json.endObject();
4696 if (lazy_.isNull()) {
4697 json.nullProperty("lazy_");
4698 } else {
4699 json.beginObjectProperty("lazy_");
4700 DumpInputScriptFields(json, lazy_);
4701 json.endObject();
4704 if (enclosingScope.isNull()) {
4705 json.nullProperty("enclosingScope");
4706 } else {
4707 json.beginObjectProperty("enclosingScope");
4708 DumpInputScopeFields(json, enclosingScope);
4709 json.endObject();
4712 // TODO: Support printing the atomCache and the source fields.
4715 void CompilationStencil::dump() const {
4716 js::Fprinter out(stderr);
4717 js::JSONPrinter json(out);
4718 dump(json);
4719 out.put("\n");
4722 void CompilationStencil::dump(js::JSONPrinter& json) const {
4723 json.beginObject();
4724 dumpFields(json);
4725 json.endObject();
4728 void CompilationStencil::dumpFields(js::JSONPrinter& json) const {
4729 char index[64];
4731 json.beginObjectProperty("scriptData");
4732 for (size_t i = 0; i < scriptData.size(); i++) {
4733 SprintfLiteral(index, "ScriptIndex(%zu)", i);
4734 json.beginObjectProperty(index);
4735 scriptData[i].dumpFields(json, this);
4736 json.endObject();
4738 json.endObject();
4740 json.beginObjectProperty("scriptExtra");
4741 for (size_t i = 0; i < scriptExtra.size(); i++) {
4742 SprintfLiteral(index, "ScriptIndex(%zu)", i);
4743 json.beginObjectProperty(index);
4744 scriptExtra[i].dumpFields(json);
4745 json.endObject();
4747 json.endObject();
4749 json.beginObjectProperty("scopeData");
4750 MOZ_ASSERT(scopeData.size() == scopeNames.size());
4751 for (size_t i = 0; i < scopeData.size(); i++) {
4752 SprintfLiteral(index, "ScopeIndex(%zu)", i);
4753 json.beginObjectProperty(index);
4754 scopeData[i].dumpFields(json, scopeNames[i], this);
4755 json.endObject();
4757 json.endObject();
4759 json.beginObjectProperty("sharedData");
4760 sharedData.dumpFields(json);
4761 json.endObject();
4763 json.beginObjectProperty("regExpData");
4764 for (size_t i = 0; i < regExpData.size(); i++) {
4765 SprintfLiteral(index, "RegExpIndex(%zu)", i);
4766 json.beginObjectProperty(index);
4767 regExpData[i].dumpFields(json, this);
4768 json.endObject();
4770 json.endObject();
4772 json.beginObjectProperty("bigIntData");
4773 for (size_t i = 0; i < bigIntData.size(); i++) {
4774 SprintfLiteral(index, "BigIntIndex(%zu)", i);
4775 GenericPrinter& out = json.beginStringProperty(index);
4776 bigIntData[i].dumpCharsNoQuote(out);
4777 json.endStringProperty();
4779 json.endObject();
4781 json.beginObjectProperty("objLiteralData");
4782 for (size_t i = 0; i < objLiteralData.size(); i++) {
4783 SprintfLiteral(index, "ObjLiteralIndex(%zu)", i);
4784 json.beginObjectProperty(index);
4785 objLiteralData[i].dumpFields(json, this);
4786 json.endObject();
4788 json.endObject();
4790 if (moduleMetadata) {
4791 json.beginObjectProperty("moduleMetadata");
4792 moduleMetadata->dumpFields(json, this);
4793 json.endObject();
4796 json.beginObjectProperty("asmJS");
4797 if (asmJS) {
4798 for (auto iter = asmJS->moduleMap.iter(); !iter.done(); iter.next()) {
4799 SprintfLiteral(index, "ScriptIndex(%u)", iter.get().key().index);
4800 json.formatProperty(index, "asm.js");
4803 json.endObject();
4806 void CompilationStencil::dumpAtom(TaggedParserAtomIndex index) const {
4807 js::Fprinter out(stderr);
4808 js::JSONPrinter json(out);
4809 json.beginObject();
4810 DumpTaggedParserAtomIndex(json, index, this);
4811 json.endObject();
4814 void ExtensibleCompilationStencil::dump() {
4815 frontend::BorrowingCompilationStencil borrowingStencil(*this);
4816 borrowingStencil.dump();
4819 void ExtensibleCompilationStencil::dump(js::JSONPrinter& json) {
4820 frontend::BorrowingCompilationStencil borrowingStencil(*this);
4821 borrowingStencil.dump(json);
4824 void ExtensibleCompilationStencil::dumpFields(js::JSONPrinter& json) {
4825 frontend::BorrowingCompilationStencil borrowingStencil(*this);
4826 borrowingStencil.dumpFields(json);
4829 void ExtensibleCompilationStencil::dumpAtom(TaggedParserAtomIndex index) {
4830 frontend::BorrowingCompilationStencil borrowingStencil(*this);
4831 borrowingStencil.dumpAtom(index);
4834 #endif // defined(DEBUG) || defined(JS_JITSPEW)
4836 JSString* CompilationAtomCache::getExistingStringAt(
4837 ParserAtomIndex index) const {
4838 MOZ_RELEASE_ASSERT(atoms_.length() >= index);
4839 return atoms_[index];
4842 JSString* CompilationAtomCache::getExistingStringAt(
4843 JSContext* cx, TaggedParserAtomIndex taggedIndex) const {
4844 if (taggedIndex.isParserAtomIndex()) {
4845 auto index = taggedIndex.toParserAtomIndex();
4846 return getExistingStringAt(index);
4849 if (taggedIndex.isWellKnownAtomId()) {
4850 auto index = taggedIndex.toWellKnownAtomId();
4851 return GetWellKnownAtom(cx, index);
4854 if (taggedIndex.isLength1StaticParserString()) {
4855 auto index = taggedIndex.toLength1StaticParserString();
4856 return cx->staticStrings().getUnit(char16_t(index));
4859 if (taggedIndex.isLength2StaticParserString()) {
4860 auto index = taggedIndex.toLength2StaticParserString();
4861 return cx->staticStrings().getLength2FromIndex(size_t(index));
4864 MOZ_ASSERT(taggedIndex.isLength3StaticParserString());
4865 auto index = taggedIndex.toLength3StaticParserString();
4866 return cx->staticStrings().getUint(uint32_t(index));
4869 JSString* CompilationAtomCache::getStringAt(ParserAtomIndex index) const {
4870 if (size_t(index) >= atoms_.length()) {
4871 return nullptr;
4873 return atoms_[index];
4876 JSAtom* CompilationAtomCache::getExistingAtomAt(ParserAtomIndex index) const {
4877 return &getExistingStringAt(index)->asAtom();
4880 JSAtom* CompilationAtomCache::getExistingAtomAt(
4881 JSContext* cx, TaggedParserAtomIndex taggedIndex) const {
4882 return &getExistingStringAt(cx, taggedIndex)->asAtom();
4885 JSAtom* CompilationAtomCache::getAtomAt(ParserAtomIndex index) const {
4886 if (size_t(index) >= atoms_.length()) {
4887 return nullptr;
4889 if (!atoms_[index]) {
4890 return nullptr;
4892 return &atoms_[index]->asAtom();
4895 bool CompilationAtomCache::hasAtomAt(ParserAtomIndex index) const {
4896 if (size_t(index) >= atoms_.length()) {
4897 return false;
4899 return !!atoms_[index];
4902 bool CompilationAtomCache::setAtomAt(FrontendContext* fc, ParserAtomIndex index,
4903 JSString* atom) {
4904 if (size_t(index) < atoms_.length()) {
4905 atoms_[index] = atom;
4906 return true;
4909 if (!atoms_.resize(size_t(index) + 1)) {
4910 ReportOutOfMemory(fc);
4911 return false;
4914 atoms_[index] = atom;
4915 return true;
4918 bool CompilationAtomCache::allocate(FrontendContext* fc, size_t length) {
4919 MOZ_ASSERT(length >= atoms_.length());
4920 if (length == atoms_.length()) {
4921 return true;
4924 if (!atoms_.resize(length)) {
4925 ReportOutOfMemory(fc);
4926 return false;
4929 return true;
4932 void CompilationAtomCache::stealBuffer(AtomCacheVector& atoms) {
4933 atoms_ = std::move(atoms);
4934 // Destroy elements, without unreserving.
4935 atoms_.clear();
4938 void CompilationAtomCache::releaseBuffer(AtomCacheVector& atoms) {
4939 atoms = std::move(atoms_);
4942 bool CompilationState::allocateGCThingsUninitialized(
4943 FrontendContext* fc, ScriptIndex scriptIndex, size_t length,
4944 TaggedScriptThingIndex** cursor) {
4945 MOZ_ASSERT(gcThingData.length() <= UINT32_MAX);
4947 auto gcThingsOffset = CompilationGCThingIndex(gcThingData.length());
4949 if (length > INDEX_LIMIT) {
4950 ReportAllocationOverflow(fc);
4951 return false;
4953 uint32_t gcThingsLength = length;
4955 if (!gcThingData.growByUninitialized(length)) {
4956 js::ReportOutOfMemory(fc);
4957 return false;
4960 if (gcThingData.length() > UINT32_MAX) {
4961 ReportAllocationOverflow(fc);
4962 return false;
4965 ScriptStencil& script = scriptData[scriptIndex];
4966 script.gcThingsOffset = gcThingsOffset;
4967 script.gcThingsLength = gcThingsLength;
4969 *cursor = gcThingData.begin() + gcThingsOffset;
4970 return true;
4973 bool CompilationState::appendScriptStencilAndData(FrontendContext* fc) {
4974 if (!scriptData.emplaceBack()) {
4975 js::ReportOutOfMemory(fc);
4976 return false;
4979 if (isInitialStencil()) {
4980 if (!scriptExtra.emplaceBack()) {
4981 scriptData.popBack();
4982 MOZ_ASSERT(scriptData.length() == scriptExtra.length());
4984 js::ReportOutOfMemory(fc);
4985 return false;
4989 return true;
4992 bool CompilationState::appendGCThings(
4993 FrontendContext* fc, ScriptIndex scriptIndex,
4994 mozilla::Span<const TaggedScriptThingIndex> things) {
4995 MOZ_ASSERT(gcThingData.length() <= UINT32_MAX);
4997 auto gcThingsOffset = CompilationGCThingIndex(gcThingData.length());
4999 if (things.size() > INDEX_LIMIT) {
5000 ReportAllocationOverflow(fc);
5001 return false;
5003 uint32_t gcThingsLength = uint32_t(things.size());
5005 if (!gcThingData.append(things.data(), things.size())) {
5006 js::ReportOutOfMemory(fc);
5007 return false;
5010 if (gcThingData.length() > UINT32_MAX) {
5011 ReportAllocationOverflow(fc);
5012 return false;
5015 ScriptStencil& script = scriptData[scriptIndex];
5016 script.gcThingsOffset = gcThingsOffset;
5017 script.gcThingsLength = gcThingsLength;
5018 return true;
5021 CompilationState::CompilationStatePosition CompilationState::getPosition() {
5022 return CompilationStatePosition{scriptData.length(),
5023 asmJS ? asmJS->moduleMap.count() : 0};
5026 void CompilationState::rewind(
5027 const CompilationState::CompilationStatePosition& pos) {
5028 if (asmJS && asmJS->moduleMap.count() != pos.asmJSCount) {
5029 for (size_t i = pos.scriptDataLength; i < scriptData.length(); i++) {
5030 asmJS->moduleMap.remove(ScriptIndex(i));
5032 MOZ_ASSERT(asmJS->moduleMap.count() == pos.asmJSCount);
5034 // scriptExtra is empty for delazification.
5035 if (scriptExtra.length()) {
5036 MOZ_ASSERT(scriptExtra.length() == scriptData.length());
5037 scriptExtra.shrinkTo(pos.scriptDataLength);
5039 scriptData.shrinkTo(pos.scriptDataLength);
5042 void CompilationState::markGhost(
5043 const CompilationState::CompilationStatePosition& pos) {
5044 for (size_t i = pos.scriptDataLength; i < scriptData.length(); i++) {
5045 scriptData[i].setIsGhost();
5049 bool CompilationStencilMerger::buildFunctionKeyToIndex(FrontendContext* fc) {
5050 if (!functionKeyToInitialScriptIndex_.reserve(initial_->scriptExtra.length() -
5051 1)) {
5052 ReportOutOfMemory(fc);
5053 return false;
5056 for (size_t i = 1; i < initial_->scriptExtra.length(); i++) {
5057 const auto& extra = initial_->scriptExtra[i];
5058 auto key = extra.extent.toFunctionKey();
5060 // There can be multiple ScriptStencilExtra with same extent if
5061 // the function is parsed multiple times because of rewind for
5062 // arrow function, and in that case the last one's index should be used.
5063 // Overwrite with the last one.
5065 // Already reserved above, but OOMTest can hit failure mode in
5066 // HashTable::add.
5067 if (!functionKeyToInitialScriptIndex_.put(key, ScriptIndex(i))) {
5068 ReportOutOfMemory(fc);
5069 return false;
5073 return true;
5076 ScriptIndex CompilationStencilMerger::getInitialScriptIndexFor(
5077 const CompilationStencil& delazification) const {
5078 auto p = functionKeyToInitialScriptIndex_.lookup(delazification.functionKey);
5079 MOZ_ASSERT(p);
5080 return p->value();
5083 bool CompilationStencilMerger::buildAtomIndexMap(
5084 FrontendContext* fc, const CompilationStencil& delazification,
5085 AtomIndexMap& atomIndexMap) {
5086 uint32_t atomCount = delazification.parserAtomData.size();
5087 if (!atomIndexMap.reserve(atomCount)) {
5088 ReportOutOfMemory(fc);
5089 return false;
5091 for (const auto& atom : delazification.parserAtomData) {
5092 auto mappedIndex = initial_->parserAtoms.internExternalParserAtom(fc, atom);
5093 if (!mappedIndex) {
5094 return false;
5096 atomIndexMap.infallibleAppend(mappedIndex);
5098 return true;
5101 bool CompilationStencilMerger::setInitial(
5102 FrontendContext* fc, UniquePtr<ExtensibleCompilationStencil>&& initial) {
5103 MOZ_ASSERT(!initial_);
5105 initial_ = std::move(initial);
5107 return buildFunctionKeyToIndex(fc);
5110 template <typename GCThingIndexMapFunc, typename AtomIndexMapFunc,
5111 typename ScopeIndexMapFunc>
5112 static void MergeScriptStencil(ScriptStencil& dest, const ScriptStencil& src,
5113 GCThingIndexMapFunc mapGCThingIndex,
5114 AtomIndexMapFunc mapAtomIndex,
5115 ScopeIndexMapFunc mapScopeIndex,
5116 bool isTopLevel) {
5117 // If this function was lazy, all inner functions should have been lazy.
5118 MOZ_ASSERT(!dest.hasSharedData());
5120 // If the inner lazy function is skipped, gcThingsLength is empty.
5121 if (src.gcThingsLength) {
5122 dest.gcThingsOffset = mapGCThingIndex(src.gcThingsOffset);
5123 dest.gcThingsLength = src.gcThingsLength;
5126 if (src.functionAtom) {
5127 dest.functionAtom = mapAtomIndex(src.functionAtom);
5130 if (!dest.hasLazyFunctionEnclosingScopeIndex() &&
5131 src.hasLazyFunctionEnclosingScopeIndex()) {
5132 // Both enclosing function and this function were lazy, and
5133 // now enclosing function is non-lazy and this function is still lazy.
5134 dest.setLazyFunctionEnclosingScopeIndex(
5135 mapScopeIndex(src.lazyFunctionEnclosingScopeIndex()));
5136 } else if (dest.hasLazyFunctionEnclosingScopeIndex() &&
5137 !src.hasLazyFunctionEnclosingScopeIndex()) {
5138 // The enclosing function was non-lazy and this function was lazy, and
5139 // now this function is non-lazy.
5140 dest.resetHasLazyFunctionEnclosingScopeIndexAfterStencilMerge();
5141 } else {
5142 // The enclosing function is still lazy.
5143 MOZ_ASSERT(!dest.hasLazyFunctionEnclosingScopeIndex());
5144 MOZ_ASSERT(!src.hasLazyFunctionEnclosingScopeIndex());
5147 #ifdef DEBUG
5148 uint16_t BASESCRIPT = uint16_t(FunctionFlags::Flags::BASESCRIPT);
5149 uint16_t HAS_INFERRED_NAME =
5150 uint16_t(FunctionFlags::Flags::HAS_INFERRED_NAME);
5151 uint16_t HAS_GUESSED_ATOM = uint16_t(FunctionFlags::Flags::HAS_GUESSED_ATOM);
5152 uint16_t acceptableDifferenceForLazy = HAS_INFERRED_NAME | HAS_GUESSED_ATOM;
5153 uint16_t acceptableDifferenceForNonLazy =
5154 BASESCRIPT | HAS_INFERRED_NAME | HAS_GUESSED_ATOM;
5156 MOZ_ASSERT_IF(
5157 isTopLevel,
5158 (dest.functionFlags.toRaw() | acceptableDifferenceForNonLazy) ==
5159 (src.functionFlags.toRaw() | acceptableDifferenceForNonLazy));
5161 // NOTE: Currently we don't delazify inner functions.
5162 MOZ_ASSERT_IF(!isTopLevel,
5163 (dest.functionFlags.toRaw() | acceptableDifferenceForLazy) ==
5164 (src.functionFlags.toRaw() | acceptableDifferenceForLazy));
5165 #endif // DEBUG
5166 dest.functionFlags = src.functionFlags;
5168 // Other flags.
5170 if (src.wasEmittedByEnclosingScript()) {
5171 // NOTE: the top-level function of the delazification have
5172 // src.wasEmittedByEnclosingScript() == false, and that shouldn't
5173 // be copied.
5174 dest.setWasEmittedByEnclosingScript();
5177 if (src.allowRelazify()) {
5178 dest.setAllowRelazify();
5181 if (src.hasSharedData()) {
5182 dest.setHasSharedData();
5186 bool CompilationStencilMerger::addDelazification(
5187 FrontendContext* fc, const CompilationStencil& delazification) {
5188 MOZ_ASSERT(initial_);
5190 auto delazifiedFunctionIndex = getInitialScriptIndexFor(delazification);
5191 auto& destFun = initial_->scriptData[delazifiedFunctionIndex];
5193 if (destFun.hasSharedData()) {
5194 // If the function was already non-lazy, it means the following happened:
5195 // A. delazified twice within single incremental encoding
5196 // 1. this function is lazily parsed
5197 // 2. incremental encoding is started
5198 // 3. this function is delazified, encoded, and merged
5199 // 4. this function is relazified
5200 // 5. this function is delazified, encoded, and merged
5202 // B. delazified twice across decode
5203 // 1. this function is lazily parsed
5204 // 2. incremental encoding is started
5205 // 3. this function is delazified, encoded, and merged
5206 // 4. incremental encoding is finished
5207 // 5. decoded
5208 // 6. incremental encoding is started
5209 // here, this function is non-lazy
5210 // 7. this function is relazified
5211 // 8. this function is delazified, encoded, and merged
5213 // A can happen with public API.
5215 // B cannot happen with public API, but can happen if incremental
5216 // encoding at step B.6 is explicitly started by internal function.
5217 // See Evaluate and StartIncrementalEncoding in js/src/shell/js.cpp.
5218 return true;
5221 // If any failure happens, the initial stencil is left in the broken state.
5222 // Immediately discard it.
5223 auto failureCase = mozilla::MakeScopeExit([&] { initial_.reset(); });
5225 mozilla::Maybe<ScopeIndex> functionEnclosingScope;
5226 if (destFun.hasLazyFunctionEnclosingScopeIndex()) {
5227 // lazyFunctionEnclosingScopeIndex_ can be Nothing if this is
5228 // top-level function.
5229 functionEnclosingScope =
5230 mozilla::Some(destFun.lazyFunctionEnclosingScopeIndex());
5233 // A map from ParserAtomIndex in delazification to TaggedParserAtomIndex
5234 // in initial_.
5235 AtomIndexMap atomIndexMap;
5236 if (!buildAtomIndexMap(fc, delazification, atomIndexMap)) {
5237 return false;
5239 auto mapAtomIndex = [&](TaggedParserAtomIndex index) {
5240 if (index.isParserAtomIndex()) {
5241 return atomIndexMap[index.toParserAtomIndex()];
5244 return index;
5247 size_t gcThingOffset = initial_->gcThingData.length();
5248 size_t regExpOffset = initial_->regExpData.length();
5249 size_t bigIntOffset = initial_->bigIntData.length();
5250 size_t objLiteralOffset = initial_->objLiteralData.length();
5251 size_t scopeOffset = initial_->scopeData.length();
5253 // Map delazification's ScriptIndex to initial's ScriptIndex.
5255 // The lazy function's gcthings list stores inner function's ScriptIndex.
5256 // The n-th gcthing holds the ScriptIndex of the (n+1)-th script in
5257 // delazification.
5259 // NOTE: Currently we don't delazify inner functions.
5260 auto lazyFunctionGCThingsOffset = destFun.gcThingsOffset;
5261 auto mapScriptIndex = [&](ScriptIndex index) {
5262 if (index == CompilationStencil::TopLevelIndex) {
5263 return delazifiedFunctionIndex;
5266 return initial_->gcThingData[lazyFunctionGCThingsOffset + index.index - 1]
5267 .toFunction();
5270 // Map other delazification's indices into initial's indices.
5271 auto mapGCThingIndex = [&](CompilationGCThingIndex offset) {
5272 return CompilationGCThingIndex(gcThingOffset + offset.index);
5274 auto mapRegExpIndex = [&](RegExpIndex index) {
5275 return RegExpIndex(regExpOffset + index.index);
5277 auto mapBigIntIndex = [&](BigIntIndex index) {
5278 return BigIntIndex(bigIntOffset + index.index);
5280 auto mapObjLiteralIndex = [&](ObjLiteralIndex index) {
5281 return ObjLiteralIndex(objLiteralOffset + index.index);
5283 auto mapScopeIndex = [&](ScopeIndex index) {
5284 return ScopeIndex(scopeOffset + index.index);
5287 // Append gcThingData, with mapping TaggedScriptThingIndex.
5288 if (!initial_->gcThingData.append(delazification.gcThingData.data(),
5289 delazification.gcThingData.size())) {
5290 js::ReportOutOfMemory(fc);
5291 return false;
5293 for (size_t i = gcThingOffset; i < initial_->gcThingData.length(); i++) {
5294 auto& index = initial_->gcThingData[i];
5295 if (index.isNull()) {
5296 // Nothing to do.
5297 } else if (index.isAtom()) {
5298 index = TaggedScriptThingIndex(mapAtomIndex(index.toAtom()));
5299 } else if (index.isBigInt()) {
5300 index = TaggedScriptThingIndex(mapBigIntIndex(index.toBigInt()));
5301 } else if (index.isObjLiteral()) {
5302 index = TaggedScriptThingIndex(mapObjLiteralIndex(index.toObjLiteral()));
5303 } else if (index.isRegExp()) {
5304 index = TaggedScriptThingIndex(mapRegExpIndex(index.toRegExp()));
5305 } else if (index.isScope()) {
5306 index = TaggedScriptThingIndex(mapScopeIndex(index.toScope()));
5307 } else if (index.isFunction()) {
5308 index = TaggedScriptThingIndex(mapScriptIndex(index.toFunction()));
5309 } else {
5310 MOZ_ASSERT(index.isEmptyGlobalScope());
5311 // Nothing to do
5315 // Append regExpData, with mapping RegExpStencil.atom_.
5316 if (!initial_->regExpData.append(delazification.regExpData.data(),
5317 delazification.regExpData.size())) {
5318 js::ReportOutOfMemory(fc);
5319 return false;
5321 for (size_t i = regExpOffset; i < initial_->regExpData.length(); i++) {
5322 auto& data = initial_->regExpData[i];
5323 data.atom_ = mapAtomIndex(data.atom_);
5326 // Append bigIntData, with copying BigIntStencil.source_.
5327 if (!initial_->bigIntData.reserve(bigIntOffset +
5328 delazification.bigIntData.size())) {
5329 js::ReportOutOfMemory(fc);
5330 return false;
5332 for (const auto& data : delazification.bigIntData) {
5333 initial_->bigIntData.infallibleEmplaceBack();
5334 if (!initial_->bigIntData.back().init(fc, initial_->alloc, data.source())) {
5335 return false;
5339 // Append objLiteralData, with copying ObjLiteralStencil.code_, and mapping
5340 // TaggedParserAtomIndex in it.
5341 if (!initial_->objLiteralData.reserve(objLiteralOffset +
5342 delazification.objLiteralData.size())) {
5343 js::ReportOutOfMemory(fc);
5344 return false;
5346 for (const auto& data : delazification.objLiteralData) {
5347 size_t length = data.code().size();
5348 auto* code = initial_->alloc.newArrayUninitialized<uint8_t>(length);
5349 if (!code) {
5350 js::ReportOutOfMemory(fc);
5351 return false;
5353 memcpy(code, data.code().data(), length);
5355 ObjLiteralModifier modifier(mozilla::Span(code, length));
5356 modifier.mapAtom(mapAtomIndex);
5358 initial_->objLiteralData.infallibleEmplaceBack(
5359 code, length, data.kind(), data.flags(), data.propertyCount());
5362 // Append scopeData, with mapping indices in ScopeStencil fields.
5363 // And append scopeNames, with copying the entire data, and mapping
5364 // trailingNames.
5365 if (!initial_->scopeData.reserve(scopeOffset +
5366 delazification.scopeData.size())) {
5367 js::ReportOutOfMemory(fc);
5368 return false;
5370 if (!initial_->scopeNames.reserve(scopeOffset +
5371 delazification.scopeNames.size())) {
5372 js::ReportOutOfMemory(fc);
5373 return false;
5375 for (size_t i = 0; i < delazification.scopeData.size(); i++) {
5376 const auto& srcData = delazification.scopeData[i];
5377 const auto* srcNames = delazification.scopeNames[i];
5379 mozilla::Maybe<ScriptIndex> functionIndex = mozilla::Nothing();
5380 if (srcData.isFunction()) {
5381 // Inner functions should be in the same order as initial, beginning from
5382 // the delazification's index.
5383 functionIndex = mozilla::Some(mapScriptIndex(srcData.functionIndex()));
5386 BaseParserScopeData* destNames = nullptr;
5387 if (srcNames) {
5388 destNames = CopyScopeData(fc, initial_->alloc, srcData.kind(), srcNames);
5389 if (!destNames) {
5390 return false;
5392 auto trailingNames =
5393 GetParserScopeDataTrailingNames(srcData.kind(), destNames);
5394 for (auto& name : trailingNames) {
5395 if (name.name()) {
5396 name.updateNameAfterStencilMerge(mapAtomIndex(name.name()));
5401 initial_->scopeData.infallibleEmplaceBack(
5402 srcData.kind(),
5403 srcData.hasEnclosing()
5404 ? mozilla::Some(mapScopeIndex(srcData.enclosing()))
5405 : functionEnclosingScope,
5406 srcData.firstFrameSlot(),
5407 srcData.hasEnvironmentShape()
5408 ? mozilla::Some(srcData.numEnvironmentSlots())
5409 : mozilla::Nothing(),
5410 functionIndex, srcData.isArrow());
5412 initial_->scopeNames.infallibleEmplaceBack(destNames);
5415 // Add delazified function's shared data.
5417 // NOTE: Currently we don't delazify inner functions.
5418 if (!initial_->sharedData.addExtraWithoutShare(
5419 fc, delazifiedFunctionIndex,
5420 delazification.sharedData.get(CompilationStencil::TopLevelIndex))) {
5421 return false;
5424 // Update scriptData, with mapping indices in ScriptStencil fields.
5425 for (uint32_t i = 0; i < delazification.scriptData.size(); i++) {
5426 auto destIndex = mapScriptIndex(ScriptIndex(i));
5427 MergeScriptStencil(initial_->scriptData[destIndex],
5428 delazification.scriptData[i], mapGCThingIndex,
5429 mapAtomIndex, mapScopeIndex,
5430 i == CompilationStencil::TopLevelIndex);
5433 // WARNING: moduleMetadata and asmJS fields are known at script/module
5434 // top-level parsing, any mutation made in this function should be reflected
5435 // to ExtensibleCompilationStencil::steal and CompilationStencil::clone.
5437 // Function shouldn't be a module.
5438 MOZ_ASSERT(!delazification.moduleMetadata);
5440 // asm.js shouldn't appear inside delazification, given asm.js forces
5441 // full-parse.
5442 MOZ_ASSERT(!delazification.asmJS);
5444 failureCase.release();
5445 return true;
5448 void JS::StencilAddRef(JS::Stencil* stencil) { stencil->refCount++; }
5449 void JS::StencilRelease(JS::Stencil* stencil) {
5450 MOZ_RELEASE_ASSERT(stencil->refCount > 0);
5451 if (--stencil->refCount == 0) {
5452 js_delete(stencil);
5456 template <typename CharT>
5457 static already_AddRefed<JS::Stencil> CompileGlobalScriptToStencilImpl(
5458 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
5459 JS::SourceText<CharT>& srcBuf) {
5460 ScopeKind scopeKind =
5461 options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
5463 AutoReportFrontendContext fc(cx);
5464 NoScopeBindingCache scopeCache;
5465 Rooted<CompilationInput> input(cx, CompilationInput(options));
5466 RefPtr<JS::Stencil> stencil = js::frontend::CompileGlobalScriptToStencil(
5467 cx, &fc, cx->tempLifoAlloc(), input.get(), &scopeCache, srcBuf,
5468 scopeKind);
5469 if (!stencil) {
5470 return nullptr;
5473 // Convert the UniquePtr to a RefPtr and increment the count (to 1).
5474 return stencil.forget();
5477 already_AddRefed<JS::Stencil> JS::CompileGlobalScriptToStencil(
5478 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
5479 JS::SourceText<mozilla::Utf8Unit>& srcBuf) {
5480 return CompileGlobalScriptToStencilImpl(cx, options, srcBuf);
5483 already_AddRefed<JS::Stencil> JS::CompileGlobalScriptToStencil(
5484 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
5485 JS::SourceText<char16_t>& srcBuf) {
5486 return CompileGlobalScriptToStencilImpl(cx, options, srcBuf);
5489 template <typename CharT>
5490 static already_AddRefed<JS::Stencil> CompileModuleScriptToStencilImpl(
5491 JSContext* cx, const JS::ReadOnlyCompileOptions& optionsInput,
5492 JS::SourceText<CharT>& srcBuf) {
5493 JS::CompileOptions options(cx, optionsInput);
5494 options.setModule();
5496 AutoReportFrontendContext fc(cx);
5497 NoScopeBindingCache scopeCache;
5498 Rooted<CompilationInput> input(cx, CompilationInput(options));
5499 RefPtr<JS::Stencil> stencil = js::frontend::ParseModuleToStencil(
5500 cx, &fc, cx->tempLifoAlloc(), input.get(), &scopeCache, srcBuf);
5501 if (!stencil) {
5502 return nullptr;
5505 // Convert the UniquePtr to a RefPtr and increment the count (to 1).
5506 return stencil.forget();
5509 already_AddRefed<JS::Stencil> JS::CompileModuleScriptToStencil(
5510 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
5511 JS::SourceText<mozilla::Utf8Unit>& srcBuf) {
5512 return CompileModuleScriptToStencilImpl(cx, options, srcBuf);
5515 already_AddRefed<JS::Stencil> JS::CompileModuleScriptToStencil(
5516 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
5517 JS::SourceText<char16_t>& srcBuf) {
5518 return CompileModuleScriptToStencilImpl(cx, options, srcBuf);
5521 JS_PUBLIC_API JSScript* JS::InstantiateGlobalStencil(
5522 JSContext* cx, const JS::InstantiateOptions& options, JS::Stencil* stencil,
5523 JS::InstantiationStorage* storage) {
5524 MOZ_ASSERT_IF(storage, storage->isValid());
5526 CompileOptions compileOptions(cx);
5527 options.copyTo(compileOptions);
5528 Rooted<CompilationInput> input(cx, CompilationInput(compileOptions));
5529 Rooted<CompilationGCOutput> gcOutput(cx);
5530 if (storage) {
5531 gcOutput.get().steal(std::move(*storage->gcOutput_));
5534 if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) {
5535 return nullptr;
5537 return gcOutput.get().script;
5540 JS_PUBLIC_API bool JS::StencilIsBorrowed(Stencil* stencil) {
5541 return stencil->storageType == CompilationStencil::StorageType::Borrowed;
5544 JS_PUBLIC_API JSObject* JS::InstantiateModuleStencil(
5545 JSContext* cx, const JS::InstantiateOptions& options, JS::Stencil* stencil,
5546 JS::InstantiationStorage* storage) {
5547 MOZ_ASSERT_IF(storage, storage->isValid());
5549 CompileOptions compileOptions(cx);
5550 options.copyTo(compileOptions);
5551 compileOptions.setModule();
5552 Rooted<CompilationInput> input(cx, CompilationInput(compileOptions));
5553 Rooted<CompilationGCOutput> gcOutput(cx);
5554 if (storage) {
5555 gcOutput.get().steal(std::move(*storage->gcOutput_));
5558 if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) {
5559 return nullptr;
5561 return gcOutput.get().module;
5564 JS::TranscodeResult JS::EncodeStencil(JSContext* cx, JS::Stencil* stencil,
5565 TranscodeBuffer& buffer) {
5566 AutoReportFrontendContext fc(cx);
5567 XDRStencilEncoder encoder(&fc, buffer);
5568 XDRResult res = encoder.codeStencil(*stencil);
5569 if (res.isErr()) {
5570 return res.unwrapErr();
5572 return TranscodeResult::Ok;
5575 JS::TranscodeResult JS::DecodeStencil(JSContext* cx,
5576 const JS::ReadOnlyDecodeOptions& options,
5577 const JS::TranscodeRange& range,
5578 JS::Stencil** stencilOut) {
5579 AutoReportFrontendContext fc(cx);
5580 return JS::DecodeStencil(&fc, options, range, stencilOut);
5583 JS::TranscodeResult JS::DecodeStencil(JS::FrontendContext* fc,
5584 const JS::ReadOnlyDecodeOptions& options,
5585 const JS::TranscodeRange& range,
5586 JS::Stencil** stencilOut) {
5587 RefPtr<ScriptSource> source = fc->getAllocator()->new_<ScriptSource>();
5588 if (!source) {
5589 return TranscodeResult::Throw;
5591 RefPtr<JS::Stencil> stencil(
5592 fc->getAllocator()->new_<CompilationStencil>(source));
5593 if (!stencil) {
5594 return TranscodeResult::Throw;
5596 XDRStencilDecoder decoder(fc, range);
5597 XDRResult res = decoder.codeStencil(options, *stencil);
5598 if (res.isErr()) {
5599 return res.unwrapErr();
5601 *stencilOut = stencil.forget().take();
5602 return TranscodeResult::Ok;
5605 JS_PUBLIC_API size_t JS::SizeOfStencil(Stencil* stencil,
5606 mozilla::MallocSizeOf mallocSizeOf) {
5607 return stencil->sizeOfIncludingThis(mallocSizeOf);
5610 JS::InstantiationStorage::~InstantiationStorage() {
5611 if (gcOutput_) {
5612 js_delete(gcOutput_);
5613 gcOutput_ = nullptr;