Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / frontend / Stencil.cpp
blob498487158e2fd91ab45fc9a8cd1fa4b10c62ed6b
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/RootingAPI.h" // Rooted
40 #include "js/Transcoding.h" // JS::TranscodeBuffer
41 #include "js/Utility.h" // js_malloc, js_calloc, js_free
42 #include "js/Value.h" // ObjectValue
43 #include "js/WasmModule.h" // JS::WasmModule
44 #include "vm/BigIntType.h" // ParseBigIntLiteral, BigIntLiteralIsZero
45 #include "vm/BindingKind.h" // BindingKind
46 #include "vm/EnvironmentObject.h"
47 #include "vm/GeneratorAndAsyncKind.h" // GeneratorKind, FunctionAsyncKind
48 #include "vm/JSContext.h" // JSContext
49 #include "vm/JSFunction.h" // JSFunction, GetFunctionPrototype, NewFunctionWithProto
50 #include "vm/JSObject.h" // JSObject, TenuredObject
51 #include "vm/JSONPrinter.h" // js::JSONPrinter
52 #include "vm/JSScript.h" // BaseScript, JSScript
53 #include "vm/RegExpObject.h" // js::RegExpObject
54 #include "vm/Scope.h" // Scope, *Scope, ScopeKind::*, ScopeKindString, ScopeIter, ScopeKindIsCatch, BindingIter, GetScopeDataTrailingNames, SizeOfParserScopeData
55 #include "vm/ScopeKind.h" // ScopeKind
56 #include "vm/SelfHosting.h" // SetClonedSelfHostedFunctionName
57 #include "vm/StaticStrings.h"
58 #include "vm/StencilEnums.h" // ImmutableScriptFlagsEnum
59 #include "vm/StringType.h" // JSAtom, js::CopyChars
60 #include "wasm/AsmJS.h" // InstantiateAsmJS
62 #include "vm/EnvironmentObject-inl.h" // JSObject::enclosingEnvironment
63 #include "vm/JSFunction-inl.h" // JSFunction::create
65 using namespace js;
66 using namespace js::frontend;
68 // These 2 functions are used to write the same code with lambda using auto
69 // arguments. The auto argument type is set by the Variant.match function of the
70 // InputScope variant. Thus dispatching to either a Scope* or to a
71 // ScopeStencilRef. This function can then be used as a way to specialize the
72 // code within the lambda without duplicating the code.
74 // Identically, an InputName is constructed using the scope type and the
75 // matching binding name type. This way, functions which are called by this
76 // lambda can manipulate an InputName and do not have to be duplicated.
78 // for (InputScopeIter si(...); si; si++) {
79 // si.scope().match([](auto& scope) {
80 // for (auto bi = InputBindingIter(scope); bi; bi++) {
81 // InputName name(scope, bi.name());
82 // }
83 // });
84 // }
85 static js::BindingIter InputBindingIter(Scope* ptr) {
86 return js::BindingIter(ptr);
89 static ParserBindingIter InputBindingIter(const ScopeStencilRef& ref) {
90 return ParserBindingIter(ref);
93 static ParserBindingIter InputBindingIter(const FakeStencilGlobalScope&) {
94 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No bindings on empty global.");
97 InputName InputScript::displayAtom() const {
98 return script_.match(
99 [](BaseScript* ptr) {
100 return InputName(ptr, ptr->function()->fullDisplayAtom());
102 [](const ScriptStencilRef& ref) {
103 return InputName(ref, ref.scriptData().functionAtom);
107 TaggedParserAtomIndex InputName::internInto(FrontendContext* fc,
108 ParserAtomsTable& parserAtoms,
109 CompilationAtomCache& atomCache) {
110 return variant_.match(
111 [&](JSAtom* ptr) -> TaggedParserAtomIndex {
112 return parserAtoms.internJSAtom(fc, atomCache, ptr);
114 [&](NameStencilRef& ref) -> TaggedParserAtomIndex {
115 return parserAtoms.internExternalParserAtomIndex(fc, ref.context_,
116 ref.atomIndex_);
120 bool InputName::isEqualTo(FrontendContext* fc, ParserAtomsTable& parserAtoms,
121 CompilationAtomCache& atomCache,
122 TaggedParserAtomIndex other,
123 JSAtom** otherCached) const {
124 return variant_.match(
125 [&](const JSAtom* ptr) -> bool {
126 if (ptr->hash() != parserAtoms.hash(other)) {
127 return false;
130 // JSAtom variant is used only on the main thread delazification,
131 // where JSContext is always available.
132 JSContext* cx = fc->maybeCurrentJSContext();
133 MOZ_ASSERT(cx);
135 if (!*otherCached) {
136 // TODO-Stencil:
137 // Here, we convert our name into a JSAtom*, and hard-crash on failure
138 // to allocate. This conversion should not be required as we should be
139 // able to iterate up snapshotted scope chains that use parser atoms.
141 // This will be fixed when the enclosing scopes are snapshotted.
143 // See bug 1690277.
144 AutoEnterOOMUnsafeRegion oomUnsafe;
145 *otherCached = parserAtoms.toJSAtom(cx, fc, other, atomCache);
146 if (!*otherCached) {
147 oomUnsafe.crash("InputName::isEqualTo");
149 } else {
150 MOZ_ASSERT(atomCache.getExistingAtomAt(cx, other) == *otherCached);
152 return ptr == *otherCached;
154 [&](const NameStencilRef& ref) -> bool {
155 return parserAtoms.isEqualToExternalParserAtomIndex(other, ref.context_,
156 ref.atomIndex_);
160 GenericAtom::GenericAtom(FrontendContext* fc, ParserAtomsTable& parserAtoms,
161 CompilationAtomCache& atomCache,
162 TaggedParserAtomIndex index)
163 : ref(EmitterName(fc, parserAtoms, atomCache, index)) {
164 hash = parserAtoms.hash(index);
167 GenericAtom::GenericAtom(const CompilationStencil& context,
168 TaggedParserAtomIndex index)
169 : ref(StencilName{context, index}) {
170 if (index.isParserAtomIndex()) {
171 ParserAtom* atom = context.parserAtomData[index.toParserAtomIndex()];
172 hash = atom->hash();
173 } else {
174 hash = index.staticOrWellKnownHash();
178 GenericAtom::GenericAtom(ScopeStencilRef& scope, TaggedParserAtomIndex index)
179 : GenericAtom(scope.context_, index) {}
181 BindingHasher<TaggedParserAtomIndex>::Lookup::Lookup(ScopeStencilRef& scope_ref,
182 const GenericAtom& other)
183 : keyStencil(scope_ref.context_), other(other) {}
185 bool GenericAtom::operator==(const GenericAtom& other) const {
186 return ref.match(
187 [&other](const EmitterName& name) -> bool {
188 return other.ref.match(
189 [&name](const EmitterName& other) -> bool {
190 // We never have multiple Emitter context at the same time.
191 MOZ_ASSERT(name.fc == other.fc);
192 MOZ_ASSERT(&name.parserAtoms == &other.parserAtoms);
193 MOZ_ASSERT(&name.atomCache == &other.atomCache);
194 return name.index == other.index;
196 [&name](const StencilName& other) -> bool {
197 return name.parserAtoms.isEqualToExternalParserAtomIndex(
198 name.index, other.stencil, other.index);
200 [&name](JSAtom* other) -> bool {
201 // JSAtom variant is used only on the main thread delazification,
202 // where JSContext is always available.
203 JSContext* cx = name.fc->maybeCurrentJSContext();
204 MOZ_ASSERT(cx);
205 AutoEnterOOMUnsafeRegion oomUnsafe;
206 JSAtom* namePtr = name.parserAtoms.toJSAtom(
207 cx, name.fc, name.index, name.atomCache);
208 if (!namePtr) {
209 oomUnsafe.crash("GenericAtom(EmitterName == JSAtom*)");
211 return namePtr == other;
214 [&other](const StencilName& name) -> bool {
215 return other.ref.match(
216 [&name](const EmitterName& other) -> bool {
217 return other.parserAtoms.isEqualToExternalParserAtomIndex(
218 other.index, name.stencil, name.index);
220 [&name](const StencilName& other) -> bool {
221 // Technically it is possible to have multiple stencils, but in
222 // this particular case let's assume we never encounter a case
223 // where we are comparing names from different stencils.
225 // The reason this assumption is safe today is that we are only
226 // using this in the context of a stencil-delazification, where
227 // the only StencilNames are coming from the CompilationStencil
228 // provided to CompilationInput::initFromStencil.
229 MOZ_ASSERT(&name.stencil == &other.stencil);
230 return name.index == other.index;
232 [](JSAtom* other) -> bool {
233 MOZ_CRASH("Never used.");
234 return false;
237 [&other](JSAtom* name) -> bool {
238 return other.ref.match(
239 [&name](const EmitterName& other) -> bool {
240 // JSAtom variant is used only on the main thread delazification,
241 // where JSContext is always available.
242 JSContext* cx = other.fc->maybeCurrentJSContext();
243 MOZ_ASSERT(cx);
244 AutoEnterOOMUnsafeRegion oomUnsafe;
245 JSAtom* otherPtr = other.parserAtoms.toJSAtom(
246 cx, other.fc, other.index, other.atomCache);
247 if (!otherPtr) {
248 oomUnsafe.crash("GenericAtom(JSAtom* == EmitterName)");
250 return name == otherPtr;
252 [](const StencilName& other) -> bool {
253 MOZ_CRASH("Never used.");
254 return false;
256 [&name](JSAtom* other) -> bool { return name == other; });
260 #ifdef DEBUG
261 template <typename SpanT, typename VecT>
262 void AssertBorrowingSpan(const SpanT& span, const VecT& vec) {
263 MOZ_ASSERT(span.size() == vec.length());
264 MOZ_ASSERT(span.data() == vec.begin());
266 #endif
268 bool ScopeBindingCache::canCacheFor(Scope* ptr) {
269 MOZ_CRASH("Unexpected scope chain type: Scope*");
272 bool ScopeBindingCache::canCacheFor(ScopeStencilRef ref) {
273 MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef");
276 bool ScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& ref) {
277 MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope");
280 BindingMap<JSAtom*>* ScopeBindingCache::createCacheFor(Scope* ptr) {
281 MOZ_CRASH("Unexpected scope chain type: Scope*");
284 BindingMap<JSAtom*>* ScopeBindingCache::lookupScope(Scope* ptr,
285 CacheGeneration gen) {
286 MOZ_CRASH("Unexpected scope chain type: Scope*");
289 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::createCacheFor(
290 ScopeStencilRef ref) {
291 MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef");
294 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::lookupScope(
295 ScopeStencilRef ref, CacheGeneration gen) {
296 MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef");
299 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::createCacheFor(
300 const FakeStencilGlobalScope& ref) {
301 MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope");
304 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::lookupScope(
305 const FakeStencilGlobalScope& ref, CacheGeneration gen) {
306 MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope");
309 bool NoScopeBindingCache::canCacheFor(Scope* ptr) { return false; }
311 bool NoScopeBindingCache::canCacheFor(ScopeStencilRef ref) { return false; }
313 bool NoScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& ref) {
314 return false;
317 bool RuntimeScopeBindingCache::canCacheFor(Scope* ptr) { return true; }
319 BindingMap<JSAtom*>* RuntimeScopeBindingCache::createCacheFor(Scope* ptr) {
320 BaseScopeData* dataPtr = ptr->rawData();
321 BindingMap<JSAtom*> bindingCache;
322 if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) {
323 return nullptr;
326 return lookupScope(ptr, cacheGeneration);
329 BindingMap<JSAtom*>* RuntimeScopeBindingCache::lookupScope(
330 Scope* ptr, CacheGeneration gen) {
331 MOZ_ASSERT(gen == cacheGeneration);
332 BaseScopeData* dataPtr = ptr->rawData();
333 auto valuePtr = scopeMap.lookup(dataPtr);
334 if (!valuePtr) {
335 return nullptr;
337 return &valuePtr->value();
340 bool StencilScopeBindingCache::canCacheFor(ScopeStencilRef ref) { return true; }
342 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::createCacheFor(
343 ScopeStencilRef ref) {
344 #ifdef DEBUG
345 AssertBorrowingSpan(ref.context_.scopeNames, merger_.getResult().scopeNames);
346 #endif
347 auto* dataPtr = ref.context_.scopeNames[ref.scopeIndex_];
348 BindingMap<TaggedParserAtomIndex> bindingCache;
349 if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) {
350 return nullptr;
353 return lookupScope(ref, 1);
356 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::lookupScope(
357 ScopeStencilRef ref, CacheGeneration gen) {
358 #ifdef DEBUG
359 AssertBorrowingSpan(ref.context_.scopeNames, merger_.getResult().scopeNames);
360 #endif
361 auto* dataPtr = ref.context_.scopeNames[ref.scopeIndex_];
362 auto ptr = scopeMap.lookup(dataPtr);
363 if (!ptr) {
364 return nullptr;
366 return &ptr->value();
369 static AbstractBaseScopeData<TaggedParserAtomIndex>
370 moduleGlobalAbstractScopeData;
372 bool StencilScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& ref) {
373 return true;
376 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::createCacheFor(
377 const FakeStencilGlobalScope& ref) {
378 auto* dataPtr = &moduleGlobalAbstractScopeData;
379 BindingMap<TaggedParserAtomIndex> bindingCache;
380 if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) {
381 return nullptr;
384 return lookupScope(ref, 1);
387 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::lookupScope(
388 const FakeStencilGlobalScope& ref, CacheGeneration gen) {
389 auto* dataPtr = &moduleGlobalAbstractScopeData;
390 auto ptr = scopeMap.lookup(dataPtr);
391 if (!ptr) {
392 return nullptr;
394 return &ptr->value();
397 bool ScopeContext::init(FrontendContext* fc, CompilationInput& input,
398 ParserAtomsTable& parserAtoms,
399 ScopeBindingCache* scopeCache, InheritThis inheritThis,
400 JSObject* enclosingEnv) {
401 // Record the scopeCache to be used while looking up NameLocation bindings.
402 this->scopeCache = scopeCache;
403 scopeCacheGen = scopeCache->getCurrentGeneration();
405 InputScope maybeNonDefaultEnclosingScope(
406 input.maybeNonDefaultEnclosingScope());
408 // If this eval is in response to Debugger.Frame.eval, we may have an
409 // incomplete scope chain. In order to provide a better debugging experience,
410 // we inspect the (optional) environment chain to determine it's enclosing
411 // FunctionScope if there is one. If there is no such scope, we use the
412 // orignal scope provided.
414 // NOTE: This is used to compute the ThisBinding kind and to allow access to
415 // private fields and methods, while other contextual information only
416 // uses the actual scope passed to the compile.
417 auto effectiveScope =
418 determineEffectiveScope(maybeNonDefaultEnclosingScope, enclosingEnv);
420 if (inheritThis == InheritThis::Yes) {
421 computeThisBinding(effectiveScope);
422 computeThisEnvironment(maybeNonDefaultEnclosingScope);
424 computeInScope(maybeNonDefaultEnclosingScope);
426 cacheEnclosingScope(input.enclosingScope);
428 if (input.target == CompilationInput::CompilationTarget::Eval) {
429 if (!cacheEnclosingScopeBindingForEval(fc, input, parserAtoms)) {
430 return false;
432 if (!cachePrivateFieldsForEval(fc, input, enclosingEnv, effectiveScope,
433 parserAtoms)) {
434 return false;
438 return true;
441 void ScopeContext::computeThisEnvironment(const InputScope& enclosingScope) {
442 uint32_t envCount = 0;
443 for (InputScopeIter si(enclosingScope); si; si++) {
444 if (si.kind() == ScopeKind::Function) {
445 // Arrow function inherit the "this" environment of the enclosing script,
446 // so continue ignore them.
447 if (!si.scope().isArrow()) {
448 allowNewTarget = true;
450 if (si.scope().allowSuperProperty()) {
451 allowSuperProperty = true;
452 enclosingThisEnvironmentHops = envCount;
455 if (si.scope().isClassConstructor()) {
456 memberInitializers =
457 si.scope().useMemberInitializers()
458 ? mozilla::Some(si.scope().getMemberInitializers())
459 : mozilla::Some(MemberInitializers::Empty());
460 MOZ_ASSERT(memberInitializers->valid);
461 } else {
462 if (si.scope().isSyntheticFunction()) {
463 allowArguments = false;
467 if (si.scope().isDerivedClassConstructor()) {
468 allowSuperCall = true;
471 // Found the effective "this" environment, so stop.
472 return;
476 if (si.scope().hasEnvironment()) {
477 envCount++;
482 void ScopeContext::computeThisBinding(const InputScope& scope) {
483 // Inspect the scope-chain.
484 for (InputScopeIter si(scope); si; si++) {
485 if (si.kind() == ScopeKind::Module) {
486 thisBinding = ThisBinding::Module;
487 return;
490 if (si.kind() == ScopeKind::Function) {
491 // Arrow functions don't have their own `this` binding.
492 if (si.scope().isArrow()) {
493 continue;
496 // Derived class constructors (and their nested arrow functions and evals)
497 // use ThisBinding::DerivedConstructor, which ensures TDZ checks happen
498 // when accessing |this|.
499 if (si.scope().isDerivedClassConstructor()) {
500 thisBinding = ThisBinding::DerivedConstructor;
501 } else {
502 thisBinding = ThisBinding::Function;
505 return;
509 thisBinding = ThisBinding::Global;
512 void ScopeContext::computeInScope(const InputScope& enclosingScope) {
513 for (InputScopeIter si(enclosingScope); si; si++) {
514 if (si.kind() == ScopeKind::ClassBody) {
515 inClass = true;
518 if (si.kind() == ScopeKind::With) {
519 inWith = true;
524 void ScopeContext::cacheEnclosingScope(const InputScope& enclosingScope) {
525 if (enclosingScope.isNull()) {
526 return;
529 enclosingScopeEnvironmentChainLength =
530 enclosingScope.environmentChainLength();
531 enclosingScopeKind = enclosingScope.kind();
533 if (enclosingScopeKind == ScopeKind::Function) {
534 enclosingScopeIsArrow = enclosingScope.isArrow();
537 enclosingScopeHasEnvironment = enclosingScope.hasEnvironment();
539 #ifdef DEBUG
540 hasNonSyntacticScopeOnChain =
541 enclosingScope.hasOnChain(ScopeKind::NonSyntactic);
543 // This computes a general answer for the query "does the enclosing scope
544 // have a function scope that needs a home object?", but it's only asserted
545 // if the parser parses eval body that contains `super` that needs a home
546 // object.
547 for (InputScopeIter si(enclosingScope); si; si++) {
548 if (si.kind() == ScopeKind::Function) {
549 if (si.scope().isArrow()) {
550 continue;
552 if (si.scope().allowSuperProperty() && si.scope().needsHomeObject()) {
553 hasFunctionNeedsHomeObjectOnChain = true;
555 break;
558 #endif
560 // Pre-fill the scope cache by iterating over all the names. Stop iterating
561 // as soon as we find a scope which already has a filled scope cache.
562 AutoEnterOOMUnsafeRegion oomUnsafe;
563 for (InputScopeIter si(enclosingScope); si; si++) {
564 // If the current scope already exists, then there is no need to go deeper
565 // as the scope which are encoded after this one should already be present
566 // in the cache.
567 bool hasScopeCache = si.scope().match([&](auto& scope_ref) -> bool {
568 MOZ_ASSERT(scopeCache->canCacheFor(scope_ref));
569 return scopeCache->lookupScope(scope_ref, scopeCacheGen);
571 if (hasScopeCache) {
572 return;
575 bool hasEnv = si.hasSyntacticEnvironment();
576 auto setCacthAll = [&](NameLocation loc) {
577 return si.scope().match([&](auto& scope_ref) {
578 using BindingMapPtr = decltype(scopeCache->createCacheFor(scope_ref));
579 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
580 if (!bindingMapPtr) {
581 oomUnsafe.crash(
582 "ScopeContext::cacheEnclosingScope: scopeCache->createCacheFor");
583 return;
586 bindingMapPtr->catchAll.emplace(loc);
589 auto createEmpty = [&]() {
590 return si.scope().match([&](auto& scope_ref) {
591 using BindingMapPtr = decltype(scopeCache->createCacheFor(scope_ref));
592 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
593 if (!bindingMapPtr) {
594 oomUnsafe.crash(
595 "ScopeContext::cacheEnclosingScope: scopeCache->createCacheFor");
596 return;
601 switch (si.kind()) {
602 case ScopeKind::Function:
603 if (hasEnv) {
604 if (si.scope().funHasExtensibleScope()) {
605 setCacthAll(NameLocation::Dynamic());
606 return;
609 si.scope().match([&](auto& scope_ref) {
610 using BindingMapPtr =
611 decltype(scopeCache->createCacheFor(scope_ref));
612 using Lookup =
613 typename std::remove_pointer_t<BindingMapPtr>::Lookup;
614 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
615 if (!bindingMapPtr) {
616 oomUnsafe.crash(
617 "ScopeContext::cacheEnclosingScope: "
618 "scopeCache->createCacheFor");
619 return;
622 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
623 NameLocation loc = bi.nameLocation();
624 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) {
625 continue;
627 auto ctxFreeKey = bi.name();
628 GenericAtom ctxKey(scope_ref, ctxFreeKey);
629 Lookup ctxLookup(scope_ref, ctxKey);
630 if (!bindingMapPtr->hashMap.put(ctxLookup, ctxFreeKey, loc)) {
631 oomUnsafe.crash(
632 "ScopeContext::cacheEnclosingScope: bindingMapPtr->put");
633 return;
637 } else {
638 createEmpty();
640 break;
642 case ScopeKind::StrictEval:
643 case ScopeKind::FunctionBodyVar:
644 case ScopeKind::Lexical:
645 case ScopeKind::NamedLambda:
646 case ScopeKind::StrictNamedLambda:
647 case ScopeKind::SimpleCatch:
648 case ScopeKind::Catch:
649 case ScopeKind::FunctionLexical:
650 case ScopeKind::ClassBody:
651 if (hasEnv) {
652 si.scope().match([&](auto& scope_ref) {
653 using BindingMapPtr =
654 decltype(scopeCache->createCacheFor(scope_ref));
655 using Lookup =
656 typename std::remove_pointer_t<BindingMapPtr>::Lookup;
657 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
658 if (!bindingMapPtr) {
659 oomUnsafe.crash(
660 "ScopeContext::cacheEnclosingScope: "
661 "scopeCache->createCacheFor");
662 return;
665 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
666 NameLocation loc = bi.nameLocation();
667 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) {
668 continue;
670 auto ctxFreeKey = bi.name();
671 GenericAtom ctxKey(scope_ref, ctxFreeKey);
672 Lookup ctxLookup(scope_ref, ctxKey);
673 if (!bindingMapPtr->hashMap.putNew(ctxLookup, ctxFreeKey, loc)) {
674 oomUnsafe.crash(
675 "ScopeContext::cacheEnclosingScope: bindingMapPtr->put");
676 return;
680 } else {
681 createEmpty();
683 break;
685 case ScopeKind::Module:
686 // This case is used only when delazifying a function inside
687 // module.
688 // Initial compilation of module doesn't have enlcosing scope.
689 if (hasEnv) {
690 si.scope().match([&](auto& scope_ref) {
691 using BindingMapPtr =
692 decltype(scopeCache->createCacheFor(scope_ref));
693 using Lookup =
694 typename std::remove_pointer_t<BindingMapPtr>::Lookup;
695 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
696 if (!bindingMapPtr) {
697 oomUnsafe.crash(
698 "ScopeContext::cacheEnclosingScope: "
699 "scopeCache->createCacheFor");
700 return;
703 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
704 // Imports are on the environment but are indirect
705 // bindings and must be accessed dynamically instead of
706 // using an EnvironmentCoordinate.
707 NameLocation loc = bi.nameLocation();
708 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate &&
709 loc.kind() != NameLocation::Kind::Import) {
710 continue;
712 auto ctxFreeKey = bi.name();
713 GenericAtom ctxKey(scope_ref, ctxFreeKey);
714 Lookup ctxLookup(scope_ref, ctxKey);
715 if (!bindingMapPtr->hashMap.putNew(ctxLookup, ctxFreeKey, loc)) {
716 oomUnsafe.crash(
717 "ScopeContext::cacheEnclosingScope: bindingMapPtr->put");
718 return;
722 } else {
723 createEmpty();
725 break;
727 case ScopeKind::Eval:
728 // As an optimization, if the eval doesn't have its own var
729 // environment and its immediate enclosing scope is a global
730 // scope, all accesses are global.
731 if (!hasEnv) {
732 ScopeKind kind = si.scope().enclosing().kind();
733 if (kind == ScopeKind::Global || kind == ScopeKind::NonSyntactic) {
734 setCacthAll(NameLocation::Global(BindingKind::Var));
735 return;
739 setCacthAll(NameLocation::Dynamic());
740 return;
742 case ScopeKind::Global:
743 setCacthAll(NameLocation::Global(BindingKind::Var));
744 return;
746 case ScopeKind::With:
747 case ScopeKind::NonSyntactic:
748 setCacthAll(NameLocation::Dynamic());
749 return;
751 case ScopeKind::WasmInstance:
752 case ScopeKind::WasmFunction:
753 MOZ_CRASH("No direct eval inside wasm functions");
757 MOZ_CRASH("Malformed scope chain");
760 InputScope ScopeContext::determineEffectiveScope(InputScope& scope,
761 JSObject* environment) {
762 MOZ_ASSERT(effectiveScopeHops == 0);
763 // If the scope-chain is non-syntactic, we may still determine a more precise
764 // effective-scope to use instead.
765 if (environment && scope.hasOnChain(ScopeKind::NonSyntactic)) {
766 JSObject* env = environment;
767 while (env) {
768 // Look at target of any DebugEnvironmentProxy, but be sure to use
769 // enclosingEnvironment() of the proxy itself.
770 JSObject* unwrapped = env;
771 if (env->is<DebugEnvironmentProxy>()) {
772 unwrapped = &env->as<DebugEnvironmentProxy>().environment();
773 #ifdef DEBUG
774 enclosingEnvironmentIsDebugProxy_ = true;
775 #endif
778 if (unwrapped->is<CallObject>()) {
779 JSFunction* callee = &unwrapped->as<CallObject>().callee();
780 return InputScope(callee->nonLazyScript()->bodyScope());
783 env = env->enclosingEnvironment();
784 effectiveScopeHops++;
788 return scope;
791 static uint32_t DepthOfNearestVarScopeForDirectEval(const InputScope& scope) {
792 uint32_t depth = 0;
793 if (scope.isNull()) {
794 return depth;
796 for (InputScopeIter si(scope); si; si++) {
797 depth++;
798 switch (si.scope().kind()) {
799 case ScopeKind::Function:
800 case ScopeKind::FunctionBodyVar:
801 case ScopeKind::Global:
802 case ScopeKind::NonSyntactic:
803 return depth;
804 default:
805 break;
808 return depth;
811 bool ScopeContext::cacheEnclosingScopeBindingForEval(
812 FrontendContext* fc, CompilationInput& input,
813 ParserAtomsTable& parserAtoms) {
814 enclosingLexicalBindingCache_.emplace();
816 uint32_t varScopeDepth =
817 DepthOfNearestVarScopeForDirectEval(input.enclosingScope);
818 uint32_t depth = 0;
819 for (InputScopeIter si(input.enclosingScope); si; si++) {
820 bool success = si.scope().match([&](auto& scope_ref) {
821 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
822 switch (bi.kind()) {
823 case BindingKind::Let: {
824 // Annex B.3.5 allows redeclaring simple (non-destructured)
825 // catch parameters with var declarations.
826 bool annexB35Allowance = si.kind() == ScopeKind::SimpleCatch;
827 if (!annexB35Allowance) {
828 auto kind = ScopeKindIsCatch(si.kind())
829 ? EnclosingLexicalBindingKind::CatchParameter
830 : EnclosingLexicalBindingKind::Let;
831 InputName binding(scope_ref, bi.name());
832 if (!addToEnclosingLexicalBindingCache(
833 fc, parserAtoms, input.atomCache, binding, kind)) {
834 return false;
837 break;
840 case BindingKind::Const: {
841 InputName binding(scope_ref, bi.name());
842 if (!addToEnclosingLexicalBindingCache(
843 fc, parserAtoms, input.atomCache, binding,
844 EnclosingLexicalBindingKind::Const)) {
845 return false;
847 break;
850 case BindingKind::Synthetic: {
851 InputName binding(scope_ref, bi.name());
852 if (!addToEnclosingLexicalBindingCache(
853 fc, parserAtoms, input.atomCache, binding,
854 EnclosingLexicalBindingKind::Synthetic)) {
855 return false;
857 break;
860 case BindingKind::PrivateMethod: {
861 InputName binding(scope_ref, bi.name());
862 if (!addToEnclosingLexicalBindingCache(
863 fc, parserAtoms, input.atomCache, binding,
864 EnclosingLexicalBindingKind::PrivateMethod)) {
865 return false;
867 break;
870 case BindingKind::Import:
871 case BindingKind::FormalParameter:
872 case BindingKind::Var:
873 case BindingKind::NamedLambdaCallee:
874 break;
877 return true;
879 if (!success) {
880 return false;
883 if (++depth == varScopeDepth) {
884 break;
888 return true;
891 bool ScopeContext::addToEnclosingLexicalBindingCache(
892 FrontendContext* fc, ParserAtomsTable& parserAtoms,
893 CompilationAtomCache& atomCache, InputName& name,
894 EnclosingLexicalBindingKind kind) {
895 TaggedParserAtomIndex parserName =
896 name.internInto(fc, parserAtoms, atomCache);
897 if (!parserName) {
898 return false;
901 // Same lexical binding can appear multiple times across scopes.
903 // enclosingLexicalBindingCache_ map is used for detecting conflicting
904 // `var` binding, and inner binding should be reported in the error.
906 // cacheEnclosingScopeBindingForEval iterates from inner scope, and
907 // inner-most binding is added to the map first.
909 // Do not overwrite the value with outer bindings.
910 auto p = enclosingLexicalBindingCache_->lookupForAdd(parserName);
911 if (!p) {
912 if (!enclosingLexicalBindingCache_->add(p, parserName, kind)) {
913 ReportOutOfMemory(fc);
914 return false;
918 return true;
921 static bool IsPrivateField(Scope*, JSAtom* atom) {
922 MOZ_ASSERT(atom->length() > 0);
924 JS::AutoCheckCannotGC nogc;
925 if (atom->hasLatin1Chars()) {
926 return atom->latin1Chars(nogc)[0] == '#';
929 return atom->twoByteChars(nogc)[0] == '#';
932 static bool IsPrivateField(ScopeStencilRef& scope, TaggedParserAtomIndex atom) {
933 if (atom.isParserAtomIndex()) {
934 const CompilationStencil& context = scope.context_;
935 ParserAtom* parserAtom = context.parserAtomData[atom.toParserAtomIndex()];
936 return parserAtom->isPrivateName();
939 #ifdef DEBUG
940 if (atom.isWellKnownAtomId()) {
941 const auto& info = GetWellKnownAtomInfo(atom.toWellKnownAtomId());
942 // #constructor is a well-known term, but it is invalid private name.
943 MOZ_ASSERT(!(info.length > 1 && info.content[0] == '#'));
944 } else if (atom.isLength2StaticParserString()) {
945 char content[2];
946 ParserAtomsTable::getLength2Content(atom.toLength2StaticParserString(),
947 content);
948 // # character is not part of the allowed character of static strings.
949 MOZ_ASSERT(content[0] != '#');
951 #endif
953 return false;
956 static bool IsPrivateField(const FakeStencilGlobalScope&,
957 TaggedParserAtomIndex) {
958 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No private fields on empty global.");
961 bool ScopeContext::cachePrivateFieldsForEval(FrontendContext* fc,
962 CompilationInput& input,
963 JSObject* enclosingEnvironment,
964 const InputScope& effectiveScope,
965 ParserAtomsTable& parserAtoms) {
966 effectiveScopePrivateFieldCache_.emplace();
968 // We compute an environment coordinate relative to the effective scope
969 // environment. In order to safely consume these environment coordinates,
970 // we re-map them to include the hops to get the to the effective scope:
971 // see EmitterScope::lookupPrivate
972 uint32_t hops = effectiveScopeHops;
973 for (InputScopeIter si(effectiveScope); si; si++) {
974 if (si.scope().kind() == ScopeKind::ClassBody) {
975 uint32_t slots = 0;
976 bool success = si.scope().match([&](auto& scope_ref) {
977 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
978 if (bi.kind() == BindingKind::PrivateMethod ||
979 (bi.kind() == BindingKind::Synthetic &&
980 IsPrivateField(scope_ref, bi.name()))) {
981 InputName binding(scope_ref, bi.name());
982 auto parserName =
983 binding.internInto(fc, parserAtoms, input.atomCache);
984 if (!parserName) {
985 return false;
988 NameLocation loc = NameLocation::DebugEnvironmentCoordinate(
989 bi.kind(), hops, slots);
991 if (!effectiveScopePrivateFieldCache_->put(parserName, loc)) {
992 ReportOutOfMemory(fc);
993 return false;
996 slots++;
998 return true;
1000 if (!success) {
1001 return false;
1005 // Hops is only consumed by GetAliasedDebugVar, which uses this to
1006 // traverse the debug environment chain. See the [SMDOC] for Debug
1007 // Environment Chain, which explains why we don't check for
1008 // isEnvironment when computing hops here (basically, debug proxies
1009 // pretend all scopes have environments, even if they were actually
1010 // optimized out).
1011 hops++;
1014 return true;
1017 #ifdef DEBUG
1018 static bool NameIsOnEnvironment(FrontendContext* fc,
1019 ParserAtomsTable& parserAtoms,
1020 CompilationAtomCache& atomCache,
1021 InputScope& scope, TaggedParserAtomIndex name) {
1022 JSAtom* jsname = nullptr;
1023 return scope.match([&](auto& scope_ref) {
1024 if (std::is_same_v<decltype(scope_ref), FakeStencilGlobalScope&>) {
1025 // This condition is added to handle the FakeStencilGlobalScope which is
1026 // used to emulate the global object when delazifying while executing, and
1027 // which is not provided by the Stencil.
1028 return true;
1030 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
1031 // If found, the name must already be on the environment or an import,
1032 // or else there is a bug in the closed-over name analysis in the
1033 // Parser.
1034 InputName binding(scope_ref, bi.name());
1035 if (binding.isEqualTo(fc, parserAtoms, atomCache, name, &jsname)) {
1036 BindingLocation::Kind kind = bi.location().kind();
1038 if (bi.hasArgumentSlot()) {
1039 // The following is equivalent to
1040 // functionScope.script()->functionAllowsParameterRedeclaration()
1041 if (scope.hasMappedArgsObj()) {
1042 // Check for duplicate positional formal parameters.
1043 using InputBindingIter = decltype(bi);
1044 for (InputBindingIter bi2(bi); bi2 && bi2.hasArgumentSlot();
1045 bi2++) {
1046 InputName binding2(scope_ref, bi2.name());
1047 if (binding2.isEqualTo(fc, parserAtoms, atomCache, name,
1048 &jsname)) {
1049 kind = bi2.location().kind();
1055 return kind == BindingLocation::Kind::Global ||
1056 kind == BindingLocation::Kind::Environment ||
1057 kind == BindingLocation::Kind::Import;
1061 // If not found, assume it's on the global or dynamically accessed.
1062 return true;
1065 #endif
1067 NameLocation ScopeContext::searchInEnclosingScope(FrontendContext* fc,
1068 CompilationInput& input,
1069 ParserAtomsTable& parserAtoms,
1070 TaggedParserAtomIndex name) {
1071 MOZ_ASSERT(input.target ==
1072 CompilationInput::CompilationTarget::Delazification ||
1073 input.target == CompilationInput::CompilationTarget::Eval);
1075 MOZ_ASSERT(scopeCache);
1076 if (scopeCacheGen != scopeCache->getCurrentGeneration()) {
1077 return searchInEnclosingScopeNoCache(fc, input, parserAtoms, name);
1080 #ifdef DEBUG
1081 // Catch assertion failures in the NoCache variant before looking at the
1082 // cached content.
1083 NameLocation expect =
1084 searchInEnclosingScopeNoCache(fc, input, parserAtoms, name);
1085 #endif
1087 NameLocation found =
1088 searchInEnclosingScopeWithCache(fc, input, parserAtoms, name);
1089 MOZ_ASSERT(expect == found);
1090 return found;
1093 NameLocation ScopeContext::searchInEnclosingScopeWithCache(
1094 FrontendContext* fc, CompilationInput& input, ParserAtomsTable& parserAtoms,
1095 TaggedParserAtomIndex name) {
1096 MOZ_ASSERT(input.target ==
1097 CompilationInput::CompilationTarget::Delazification ||
1098 input.target == CompilationInput::CompilationTarget::Eval);
1100 // Generic atom of the looked up name.
1101 GenericAtom genName(fc, parserAtoms, input.atomCache, name);
1102 mozilla::Maybe<NameLocation> found;
1104 // Number of enclosing scope we walked over.
1105 uint8_t hops = 0;
1107 for (InputScopeIter si(input.enclosingScope); si; si++) {
1108 MOZ_ASSERT(NameIsOnEnvironment(fc, parserAtoms, input.atomCache, si.scope(),
1109 name));
1111 // If the result happens to be in the cached content of the scope that we
1112 // are iterating over, then return it.
1113 si.scope().match([&](auto& scope_ref) {
1114 using BindingMapPtr =
1115 decltype(scopeCache->lookupScope(scope_ref, scopeCacheGen));
1116 BindingMapPtr bindingMapPtr =
1117 scopeCache->lookupScope(scope_ref, scopeCacheGen);
1118 MOZ_ASSERT(bindingMapPtr);
1120 auto& bindingMap = *bindingMapPtr;
1121 if (bindingMap.catchAll.isSome()) {
1122 found = bindingMap.catchAll;
1123 return;
1126 // The scope_ref is given as argument to know where to lookup the key
1127 // index of the hash table if the names have to be compared.
1128 using Lookup = typename std::remove_pointer_t<BindingMapPtr>::Lookup;
1129 Lookup ctxName(scope_ref, genName);
1130 auto ptr = bindingMap.hashMap.lookup(ctxName);
1131 if (!ptr) {
1132 return;
1135 found.emplace(ptr->value());
1138 if (found.isSome()) {
1139 // Cached entries do not store the number of hops, as it might be reused
1140 // by multiple inner functions, which might different number of hops.
1141 found = found.map([&hops](NameLocation loc) {
1142 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) {
1143 return loc;
1145 return loc.addHops(hops);
1147 return found.value();
1150 bool hasEnv = si.hasSyntacticEnvironment();
1152 if (hasEnv) {
1153 MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1);
1154 hops++;
1158 MOZ_CRASH("Malformed scope chain");
1161 NameLocation ScopeContext::searchInEnclosingScopeNoCache(
1162 FrontendContext* fc, CompilationInput& input, ParserAtomsTable& parserAtoms,
1163 TaggedParserAtomIndex name) {
1164 MOZ_ASSERT(input.target ==
1165 CompilationInput::CompilationTarget::Delazification ||
1166 input.target == CompilationInput::CompilationTarget::Eval);
1168 // Cached JSAtom equivalent of the TaggedParserAtomIndex `name` argument.
1169 JSAtom* jsname = nullptr;
1171 // NameLocation which contains relative locations to access `name`.
1172 mozilla::Maybe<NameLocation> result;
1174 // Number of enclosing scoep we walked over.
1175 uint8_t hops = 0;
1177 for (InputScopeIter si(input.enclosingScope); si; si++) {
1178 MOZ_ASSERT(NameIsOnEnvironment(fc, parserAtoms, input.atomCache, si.scope(),
1179 name));
1181 bool hasEnv = si.hasSyntacticEnvironment();
1182 switch (si.kind()) {
1183 case ScopeKind::Function:
1184 if (hasEnv) {
1185 if (si.scope().funHasExtensibleScope()) {
1186 return NameLocation::Dynamic();
1189 si.scope().match([&](auto& scope_ref) {
1190 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
1191 InputName binding(scope_ref, bi.name());
1192 if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name,
1193 &jsname)) {
1194 continue;
1197 BindingLocation bindLoc = bi.location();
1198 // hasMappedArgsObj == script.functionAllowsParameterRedeclaration
1199 if (bi.hasArgumentSlot() && si.scope().hasMappedArgsObj()) {
1200 // Check for duplicate positional formal parameters.
1201 using InputBindingIter = decltype(bi);
1202 for (InputBindingIter bi2(bi); bi2 && bi2.hasArgumentSlot();
1203 bi2++) {
1204 if (bi.name() == bi2.name()) {
1205 bindLoc = bi2.location();
1210 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
1211 result.emplace(NameLocation::EnvironmentCoordinate(
1212 bi.kind(), hops, bindLoc.slot()));
1213 return;
1217 break;
1219 case ScopeKind::StrictEval:
1220 case ScopeKind::FunctionBodyVar:
1221 case ScopeKind::Lexical:
1222 case ScopeKind::NamedLambda:
1223 case ScopeKind::StrictNamedLambda:
1224 case ScopeKind::SimpleCatch:
1225 case ScopeKind::Catch:
1226 case ScopeKind::FunctionLexical:
1227 case ScopeKind::ClassBody:
1228 if (hasEnv) {
1229 si.scope().match([&](auto& scope_ref) {
1230 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
1231 InputName binding(scope_ref, bi.name());
1232 if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name,
1233 &jsname)) {
1234 continue;
1237 // The name must already have been marked as closed
1238 // over. If this assertion is hit, there is a bug in the
1239 // name analysis.
1240 BindingLocation bindLoc = bi.location();
1241 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
1242 result.emplace(NameLocation::EnvironmentCoordinate(
1243 bi.kind(), hops, bindLoc.slot()));
1244 return;
1248 break;
1250 case ScopeKind::Module:
1251 // This case is used only when delazifying a function inside
1252 // module.
1253 // Initial compilation of module doesn't have enlcosing scope.
1254 if (hasEnv) {
1255 si.scope().match([&](auto& scope_ref) {
1256 for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
1257 InputName binding(scope_ref, bi.name());
1258 if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name,
1259 &jsname)) {
1260 continue;
1263 BindingLocation bindLoc = bi.location();
1265 // Imports are on the environment but are indirect
1266 // bindings and must be accessed dynamically instead of
1267 // using an EnvironmentCoordinate.
1268 if (bindLoc.kind() == BindingLocation::Kind::Import) {
1269 MOZ_ASSERT(si.kind() == ScopeKind::Module);
1270 result.emplace(NameLocation::Import());
1271 return;
1274 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
1275 result.emplace(NameLocation::EnvironmentCoordinate(
1276 bi.kind(), hops, bindLoc.slot()));
1277 return;
1281 break;
1283 case ScopeKind::Eval:
1284 // As an optimization, if the eval doesn't have its own var
1285 // environment and its immediate enclosing scope is a global
1286 // scope, all accesses are global.
1287 if (!hasEnv) {
1288 ScopeKind kind = si.scope().enclosing().kind();
1289 if (kind == ScopeKind::Global || kind == ScopeKind::NonSyntactic) {
1290 return NameLocation::Global(BindingKind::Var);
1293 return NameLocation::Dynamic();
1295 case ScopeKind::Global:
1296 return NameLocation::Global(BindingKind::Var);
1298 case ScopeKind::With:
1299 case ScopeKind::NonSyntactic:
1300 return NameLocation::Dynamic();
1302 case ScopeKind::WasmInstance:
1303 case ScopeKind::WasmFunction:
1304 MOZ_CRASH("No direct eval inside wasm functions");
1307 if (result.isSome()) {
1308 return result.value();
1311 if (hasEnv) {
1312 MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1);
1313 hops++;
1317 MOZ_CRASH("Malformed scope chain");
1320 mozilla::Maybe<ScopeContext::EnclosingLexicalBindingKind>
1321 ScopeContext::lookupLexicalBindingInEnclosingScope(TaggedParserAtomIndex name) {
1322 auto p = enclosingLexicalBindingCache_->lookup(name);
1323 if (!p) {
1324 return mozilla::Nothing();
1327 return mozilla::Some(p->value());
1330 bool ScopeContext::effectiveScopePrivateFieldCacheHas(
1331 TaggedParserAtomIndex name) {
1332 return effectiveScopePrivateFieldCache_->has(name);
1335 mozilla::Maybe<NameLocation> ScopeContext::getPrivateFieldLocation(
1336 TaggedParserAtomIndex name) {
1337 // The locations returned by this method are only valid for
1338 // traversing debug environments.
1340 // See the comment in cachePrivateFieldsForEval
1341 MOZ_ASSERT(enclosingEnvironmentIsDebugProxy_);
1342 auto p = effectiveScopePrivateFieldCache_->lookup(name);
1343 if (!p) {
1344 return mozilla::Nothing();
1346 return mozilla::Some(p->value());
1349 bool CompilationInput::initScriptSource(FrontendContext* fc) {
1350 source = do_AddRef(fc->getAllocator()->new_<ScriptSource>());
1351 if (!source) {
1352 return false;
1355 return source->initFromOptions(fc, options);
1358 bool CompilationInput::initForStandaloneFunctionInNonSyntacticScope(
1359 FrontendContext* fc, Handle<Scope*> functionEnclosingScope) {
1360 MOZ_ASSERT(!functionEnclosingScope->as<GlobalScope>().isSyntactic());
1362 target = CompilationTarget::StandaloneFunctionInNonSyntacticScope;
1363 if (!initScriptSource(fc)) {
1364 return false;
1366 enclosingScope = InputScope(functionEnclosingScope);
1367 return true;
1370 FunctionSyntaxKind CompilationInput::functionSyntaxKind() const {
1371 if (functionFlags().isClassConstructor()) {
1372 if (functionFlags().hasBaseScript() && isDerivedClassConstructor()) {
1373 return FunctionSyntaxKind::DerivedClassConstructor;
1375 return FunctionSyntaxKind::ClassConstructor;
1377 if (functionFlags().isMethod()) {
1378 if (functionFlags().hasBaseScript() && isSyntheticFunction()) {
1379 // return FunctionSyntaxKind::FieldInitializer;
1380 MOZ_ASSERT_UNREACHABLE(
1381 "Lazy parsing of class field initializers not supported (yet)");
1383 return FunctionSyntaxKind::Method;
1385 if (functionFlags().isGetter()) {
1386 return FunctionSyntaxKind::Getter;
1388 if (functionFlags().isSetter()) {
1389 return FunctionSyntaxKind::Setter;
1391 if (functionFlags().isArrow()) {
1392 return FunctionSyntaxKind::Arrow;
1394 return FunctionSyntaxKind::Statement;
1397 bool CompilationInput::internExtraBindings(FrontendContext* fc,
1398 ParserAtomsTable& parserAtoms) {
1399 MOZ_ASSERT(hasExtraBindings());
1401 for (auto& bindingInfo : *maybeExtraBindings_) {
1402 if (bindingInfo.isShadowed) {
1403 continue;
1406 const char* chars = bindingInfo.nameChars.get();
1407 auto index = parserAtoms.internUtf8(
1408 fc, reinterpret_cast<const mozilla::Utf8Unit*>(chars), strlen(chars));
1409 if (!index) {
1410 return false;
1413 bindingInfo.nameIndex = index;
1416 return true;
1419 void InputScope::trace(JSTracer* trc) {
1420 using ScopePtr = Scope*;
1421 if (scope_.is<ScopePtr>()) {
1422 ScopePtr* ptrAddr = &scope_.as<ScopePtr>();
1423 TraceNullableRoot(trc, ptrAddr, "compilation-input-scope");
1427 void InputScript::trace(JSTracer* trc) {
1428 using ScriptPtr = BaseScript*;
1429 if (script_.is<ScriptPtr>()) {
1430 ScriptPtr* ptrAddr = &script_.as<ScriptPtr>();
1431 TraceNullableRoot(trc, ptrAddr, "compilation-input-lazy");
1435 void CompilationInput::trace(JSTracer* trc) {
1436 atomCache.trace(trc);
1437 lazy_.trace(trc);
1438 enclosingScope.trace(trc);
1441 bool CompilationSyntaxParseCache::init(FrontendContext* fc, LifoAlloc& alloc,
1442 ParserAtomsTable& parseAtoms,
1443 CompilationAtomCache& atomCache,
1444 const InputScript& lazy) {
1445 if (!copyFunctionInfo(fc, parseAtoms, atomCache, lazy)) {
1446 return false;
1448 bool success = lazy.raw().match([&](auto& ref) {
1449 if (!copyScriptInfo(fc, alloc, parseAtoms, atomCache, ref)) {
1450 return false;
1452 if (!copyClosedOverBindings(fc, alloc, parseAtoms, atomCache, ref)) {
1453 return false;
1455 return true;
1457 if (!success) {
1458 return false;
1460 #ifdef DEBUG
1461 isInitialized = true;
1462 #endif
1463 return true;
1466 bool CompilationSyntaxParseCache::copyFunctionInfo(
1467 FrontendContext* fc, ParserAtomsTable& parseAtoms,
1468 CompilationAtomCache& atomCache, const InputScript& lazy) {
1469 InputName name = lazy.displayAtom();
1470 if (!name.isNull()) {
1471 displayAtom_ = name.internInto(fc, parseAtoms, atomCache);
1472 if (!displayAtom_) {
1473 return false;
1477 funExtra_.immutableFlags = lazy.immutableFlags();
1478 funExtra_.extent = lazy.extent();
1479 if (funExtra_.useMemberInitializers()) {
1480 funExtra_.setMemberInitializers(lazy.getMemberInitializers());
1483 return true;
1486 bool CompilationSyntaxParseCache::copyScriptInfo(
1487 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
1488 CompilationAtomCache& atomCache, BaseScript* lazy) {
1489 using GCThingsSpan = mozilla::Span<TaggedScriptThingIndex>;
1490 using ScriptDataSpan = mozilla::Span<ScriptStencil>;
1491 using ScriptExtraSpan = mozilla::Span<ScriptStencilExtra>;
1492 cachedGCThings_ = GCThingsSpan(nullptr);
1493 cachedScriptData_ = ScriptDataSpan(nullptr);
1494 cachedScriptExtra_ = ScriptExtraSpan(nullptr);
1496 auto gcthings = lazy->gcthings();
1497 size_t length = gcthings.Length();
1498 if (length == 0) {
1499 return true;
1502 // Reduce the length to the first element which is not a function.
1503 for (size_t i = 0; i < length; i++) {
1504 gc::Cell* cell = gcthings[i].asCell();
1505 if (!cell || !cell->is<JSObject>()) {
1506 length = i;
1507 break;
1509 MOZ_ASSERT(cell->as<JSObject>()->is<JSFunction>());
1512 TaggedScriptThingIndex* gcThingsData =
1513 alloc.newArrayUninitialized<TaggedScriptThingIndex>(length);
1514 ScriptStencil* scriptData =
1515 alloc.newArrayUninitialized<ScriptStencil>(length);
1516 ScriptStencilExtra* scriptExtra =
1517 alloc.newArrayUninitialized<ScriptStencilExtra>(length);
1518 if (!gcThingsData || !scriptData || !scriptExtra) {
1519 ReportOutOfMemory(fc);
1520 return false;
1523 for (size_t i = 0; i < length; i++) {
1524 gc::Cell* cell = gcthings[i].asCell();
1525 JSFunction* fun = &cell->as<JSObject>()->as<JSFunction>();
1526 gcThingsData[i] = TaggedScriptThingIndex(ScriptIndex(i));
1527 new (mozilla::KnownNotNull, &scriptData[i]) ScriptStencil();
1528 ScriptStencil& data = scriptData[i];
1529 new (mozilla::KnownNotNull, &scriptExtra[i]) ScriptStencilExtra();
1530 ScriptStencilExtra& extra = scriptExtra[i];
1532 if (fun->fullDisplayAtom()) {
1533 TaggedParserAtomIndex displayAtom =
1534 parseAtoms.internJSAtom(fc, atomCache, fun->fullDisplayAtom());
1535 if (!displayAtom) {
1536 return false;
1538 data.functionAtom = displayAtom;
1540 data.functionFlags = fun->flags();
1542 BaseScript* lazy = fun->baseScript();
1543 extra.immutableFlags = lazy->immutableFlags();
1544 extra.extent = lazy->extent();
1546 // Info derived from parent compilation should not be set yet for our inner
1547 // lazy functions. Instead that info will be updated when we finish our
1548 // compilation.
1549 MOZ_ASSERT(lazy->hasEnclosingScript());
1552 cachedGCThings_ = GCThingsSpan(gcThingsData, length);
1553 cachedScriptData_ = ScriptDataSpan(scriptData, length);
1554 cachedScriptExtra_ = ScriptExtraSpan(scriptExtra, length);
1555 return true;
1558 bool CompilationSyntaxParseCache::copyScriptInfo(
1559 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
1560 CompilationAtomCache& atomCache, const ScriptStencilRef& lazy) {
1561 using GCThingsSpan = mozilla::Span<TaggedScriptThingIndex>;
1562 using ScriptDataSpan = mozilla::Span<ScriptStencil>;
1563 using ScriptExtraSpan = mozilla::Span<ScriptStencilExtra>;
1564 cachedGCThings_ = GCThingsSpan(nullptr);
1565 cachedScriptData_ = ScriptDataSpan(nullptr);
1566 cachedScriptExtra_ = ScriptExtraSpan(nullptr);
1568 size_t offset = lazy.scriptData().gcThingsOffset.index;
1569 size_t length = lazy.scriptData().gcThingsLength;
1570 if (length == 0) {
1571 return true;
1574 // Reduce the length to the first element which is not a function.
1575 for (size_t i = offset; i < offset + length; i++) {
1576 if (!lazy.context_.gcThingData[i].isFunction()) {
1577 length = i - offset;
1578 break;
1582 TaggedScriptThingIndex* gcThingsData =
1583 alloc.newArrayUninitialized<TaggedScriptThingIndex>(length);
1584 ScriptStencil* scriptData =
1585 alloc.newArrayUninitialized<ScriptStencil>(length);
1586 ScriptStencilExtra* scriptExtra =
1587 alloc.newArrayUninitialized<ScriptStencilExtra>(length);
1588 if (!gcThingsData || !scriptData || !scriptExtra) {
1589 ReportOutOfMemory(fc);
1590 return false;
1593 for (size_t i = 0; i < length; i++) {
1594 ScriptStencilRef inner{lazy.context_,
1595 lazy.context_.gcThingData[i + offset].toFunction()};
1596 gcThingsData[i] = TaggedScriptThingIndex(ScriptIndex(i));
1597 new (mozilla::KnownNotNull, &scriptData[i]) ScriptStencil();
1598 ScriptStencil& data = scriptData[i];
1599 ScriptStencilExtra& extra = scriptExtra[i];
1601 InputName name{inner, inner.scriptData().functionAtom};
1602 if (!name.isNull()) {
1603 auto displayAtom = name.internInto(fc, parseAtoms, atomCache);
1604 if (!displayAtom) {
1605 return false;
1607 data.functionAtom = displayAtom;
1609 data.functionFlags = inner.scriptData().functionFlags;
1611 extra = inner.scriptExtra();
1614 cachedGCThings_ = GCThingsSpan(gcThingsData, length);
1615 cachedScriptData_ = ScriptDataSpan(scriptData, length);
1616 cachedScriptExtra_ = ScriptExtraSpan(scriptExtra, length);
1617 return true;
1620 bool CompilationSyntaxParseCache::copyClosedOverBindings(
1621 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
1622 CompilationAtomCache& atomCache, BaseScript* lazy) {
1623 using ClosedOverBindingsSpan = mozilla::Span<TaggedParserAtomIndex>;
1624 closedOverBindings_ = ClosedOverBindingsSpan(nullptr);
1626 // The gcthings() array contains the inner function list followed by the
1627 // closed-over bindings data. Skip the inner function list, as it is already
1628 // cached in cachedGCThings_. See also: BaseScript::CreateLazy.
1629 size_t start = cachedGCThings_.Length();
1630 auto gcthings = lazy->gcthings();
1631 size_t length = gcthings.Length();
1632 MOZ_ASSERT(start <= length);
1633 if (length - start == 0) {
1634 return true;
1637 TaggedParserAtomIndex* closedOverBindings =
1638 alloc.newArrayUninitialized<TaggedParserAtomIndex>(length - start);
1639 if (!closedOverBindings) {
1640 ReportOutOfMemory(fc);
1641 return false;
1644 for (size_t i = start; i < length; i++) {
1645 gc::Cell* cell = gcthings[i].asCell();
1646 if (!cell) {
1647 closedOverBindings[i - start] = TaggedParserAtomIndex::null();
1648 continue;
1651 MOZ_ASSERT(cell->as<JSString>()->isAtom());
1653 auto name = static_cast<JSAtom*>(cell);
1654 auto parserAtom = parseAtoms.internJSAtom(fc, atomCache, name);
1655 if (!parserAtom) {
1656 return false;
1659 closedOverBindings[i - start] = parserAtom;
1662 closedOverBindings_ =
1663 ClosedOverBindingsSpan(closedOverBindings, length - start);
1664 return true;
1667 bool CompilationSyntaxParseCache::copyClosedOverBindings(
1668 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
1669 CompilationAtomCache& atomCache, const ScriptStencilRef& lazy) {
1670 using ClosedOverBindingsSpan = mozilla::Span<TaggedParserAtomIndex>;
1671 closedOverBindings_ = ClosedOverBindingsSpan(nullptr);
1673 // The gcthings array contains the inner function list followed by the
1674 // closed-over bindings data. Skip the inner function list, as it is already
1675 // cached in cachedGCThings_. See also: BaseScript::CreateLazy.
1676 size_t offset = lazy.scriptData().gcThingsOffset.index;
1677 size_t length = lazy.scriptData().gcThingsLength;
1678 size_t start = cachedGCThings_.Length();
1679 MOZ_ASSERT(start <= length);
1680 if (length - start == 0) {
1681 return true;
1683 length -= start;
1684 start += offset;
1686 // Atoms from the lazy.context (CompilationStencil) are not registered in the
1687 // the parseAtoms table. Thus we create a new span which will contain all the
1688 // interned atoms.
1689 TaggedParserAtomIndex* closedOverBindings =
1690 alloc.newArrayUninitialized<TaggedParserAtomIndex>(length);
1691 if (!closedOverBindings) {
1692 ReportOutOfMemory(fc);
1693 return false;
1696 for (size_t i = 0; i < length; i++) {
1697 auto gcThing = lazy.context_.gcThingData[i + start];
1698 if (gcThing.isNull()) {
1699 closedOverBindings[i] = TaggedParserAtomIndex::null();
1700 continue;
1703 MOZ_ASSERT(gcThing.isAtom());
1704 InputName name(lazy, gcThing.toAtom());
1705 auto parserAtom = name.internInto(fc, parseAtoms, atomCache);
1706 if (!parserAtom) {
1707 return false;
1710 closedOverBindings[i] = parserAtom;
1713 closedOverBindings_ = ClosedOverBindingsSpan(closedOverBindings, length);
1714 return true;
1717 template <typename T>
1718 PreAllocateableGCArray<T>::~PreAllocateableGCArray() {
1719 if (elems_) {
1720 js_free(elems_);
1721 elems_ = nullptr;
1725 template <typename T>
1726 bool PreAllocateableGCArray<T>::allocate(size_t length) {
1727 MOZ_ASSERT(empty());
1729 length_ = length;
1731 if (isInline()) {
1732 inlineElem_ = nullptr;
1733 return true;
1736 elems_ = reinterpret_cast<T*>(js_calloc(sizeof(T) * length_));
1737 if (!elems_) {
1738 return false;
1741 return true;
1744 template <typename T>
1745 bool PreAllocateableGCArray<T>::allocateWith(T init, size_t length) {
1746 MOZ_ASSERT(empty());
1748 length_ = length;
1750 if (isInline()) {
1751 inlineElem_ = init;
1752 return true;
1755 elems_ = reinterpret_cast<T*>(js_malloc(sizeof(T) * length_));
1756 if (!elems_) {
1757 return false;
1760 std::fill(elems_, elems_ + length_, init);
1761 return true;
1764 template <typename T>
1765 void PreAllocateableGCArray<T>::steal(Preallocated&& buffer) {
1766 MOZ_ASSERT(empty());
1768 length_ = buffer.length_;
1769 buffer.length_ = 0;
1771 if (isInline()) {
1772 inlineElem_ = nullptr;
1773 return;
1776 elems_ = reinterpret_cast<T*>(buffer.elems_);
1777 buffer.elems_ = nullptr;
1779 #ifdef DEBUG
1780 for (size_t i = 0; i < length_; i++) {
1781 MOZ_ASSERT(elems_[i] == nullptr);
1783 #endif
1786 template <typename T>
1787 void PreAllocateableGCArray<T>::trace(JSTracer* trc) {
1788 if (empty()) {
1789 return;
1792 if (isInline()) {
1793 TraceNullableRoot(trc, &inlineElem_, "PreAllocateableGCArray::inlineElem_");
1794 return;
1797 for (size_t i = 0; i < length_; i++) {
1798 TraceNullableRoot(trc, &elems_[i], "PreAllocateableGCArray::elems_");
1802 template <typename T>
1803 PreAllocateableGCArray<T>::Preallocated::~Preallocated() {
1804 if (elems_) {
1805 js_free(elems_);
1806 elems_ = nullptr;
1810 template <typename T>
1811 bool PreAllocateableGCArray<T>::Preallocated::allocate(size_t length) {
1812 MOZ_ASSERT(empty());
1814 length_ = length;
1816 if (isInline()) {
1817 return true;
1820 elems_ = reinterpret_cast<uintptr_t*>(js_calloc(sizeof(uintptr_t) * length_));
1821 if (!elems_) {
1822 return false;
1825 return true;
1828 template struct js::frontend::PreAllocateableGCArray<JSFunction*>;
1829 template struct js::frontend::PreAllocateableGCArray<js::Scope*>;
1831 void CompilationAtomCache::trace(JSTracer* trc) { atoms_.trace(trc); }
1833 void CompilationGCOutput::trace(JSTracer* trc) {
1834 TraceNullableRoot(trc, &script, "compilation-gc-output-script");
1835 TraceNullableRoot(trc, &module, "compilation-gc-output-module");
1836 TraceNullableRoot(trc, &sourceObject, "compilation-gc-output-source");
1837 functions.trace(trc);
1838 scopes.trace(trc);
1841 RegExpObject* RegExpStencil::createRegExp(
1842 JSContext* cx, const CompilationAtomCache& atomCache) const {
1843 Rooted<JSAtom*> atom(cx, atomCache.getExistingAtomAt(cx, atom_));
1844 return RegExpObject::createSyntaxChecked(cx, atom, flags(), TenuredObject);
1847 RegExpObject* RegExpStencil::createRegExpAndEnsureAtom(
1848 JSContext* cx, FrontendContext* fc, ParserAtomsTable& parserAtoms,
1849 CompilationAtomCache& atomCache) const {
1850 Rooted<JSAtom*> atom(cx, parserAtoms.toJSAtom(cx, fc, atom_, atomCache));
1851 if (!atom) {
1852 return nullptr;
1854 return RegExpObject::createSyntaxChecked(cx, atom, flags(), TenuredObject);
1857 AbstractScopePtr ScopeStencil::enclosing(
1858 CompilationState& compilationState) const {
1859 if (hasEnclosing()) {
1860 return AbstractScopePtr(compilationState, enclosing());
1863 return AbstractScopePtr::compilationEnclosingScope(compilationState);
1866 Scope* ScopeStencil::enclosingExistingScope(
1867 const CompilationInput& input, const CompilationGCOutput& gcOutput) const {
1868 if (hasEnclosing()) {
1869 Scope* result = gcOutput.getScopeNoBaseIndex(enclosing());
1870 MOZ_ASSERT(result, "Scope must already exist to use this method");
1871 return result;
1874 // When creating a scope based on the input and a gc-output, we assume that
1875 // the scope stencil that we are looking at has not been merged into another
1876 // stencil, and thus that we still have the compilation input of the stencil.
1878 // Otherwise, if this was in the case of an input generated from a Stencil
1879 // instead of live-gc values, we would not know its associated gcOutput as it
1880 // might not even have one yet.
1881 return input.enclosingScope.variant().as<Scope*>();
1884 Scope* ScopeStencil::createScope(JSContext* cx, CompilationInput& input,
1885 CompilationGCOutput& gcOutput,
1886 BaseParserScopeData* baseScopeData) const {
1887 Rooted<Scope*> enclosingScope(cx, enclosingExistingScope(input, gcOutput));
1888 return createScope(cx, input.atomCache, enclosingScope, baseScopeData);
1891 Scope* ScopeStencil::createScope(JSContext* cx, CompilationAtomCache& atomCache,
1892 Handle<Scope*> enclosingScope,
1893 BaseParserScopeData* baseScopeData) const {
1894 switch (kind()) {
1895 case ScopeKind::Function: {
1896 using ScopeType = FunctionScope;
1897 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1898 return createSpecificScope<ScopeType, CallObject>(
1899 cx, atomCache, enclosingScope, baseScopeData);
1901 case ScopeKind::Lexical:
1902 case ScopeKind::SimpleCatch:
1903 case ScopeKind::Catch:
1904 case ScopeKind::NamedLambda:
1905 case ScopeKind::StrictNamedLambda:
1906 case ScopeKind::FunctionLexical: {
1907 using ScopeType = LexicalScope;
1908 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1909 return createSpecificScope<ScopeType, BlockLexicalEnvironmentObject>(
1910 cx, atomCache, enclosingScope, baseScopeData);
1912 case ScopeKind::ClassBody: {
1913 using ScopeType = ClassBodyScope;
1914 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1915 return createSpecificScope<ScopeType, BlockLexicalEnvironmentObject>(
1916 cx, atomCache, enclosingScope, baseScopeData);
1918 case ScopeKind::FunctionBodyVar: {
1919 using ScopeType = VarScope;
1920 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1921 return createSpecificScope<ScopeType, VarEnvironmentObject>(
1922 cx, atomCache, enclosingScope, baseScopeData);
1924 case ScopeKind::Global:
1925 case ScopeKind::NonSyntactic: {
1926 using ScopeType = GlobalScope;
1927 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1928 return createSpecificScope<ScopeType, std::nullptr_t>(
1929 cx, atomCache, enclosingScope, baseScopeData);
1931 case ScopeKind::Eval:
1932 case ScopeKind::StrictEval: {
1933 using ScopeType = EvalScope;
1934 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1935 return createSpecificScope<ScopeType, VarEnvironmentObject>(
1936 cx, atomCache, enclosingScope, baseScopeData);
1938 case ScopeKind::Module: {
1939 using ScopeType = ModuleScope;
1940 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1941 return createSpecificScope<ScopeType, ModuleEnvironmentObject>(
1942 cx, atomCache, enclosingScope, baseScopeData);
1944 case ScopeKind::With: {
1945 using ScopeType = WithScope;
1946 MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
1947 return createSpecificScope<ScopeType, std::nullptr_t>(
1948 cx, atomCache, enclosingScope, baseScopeData);
1950 case ScopeKind::WasmFunction:
1951 case ScopeKind::WasmInstance: {
1952 // ScopeStencil does not support WASM
1953 break;
1956 MOZ_CRASH();
1959 bool CompilationState::prepareSharedDataStorage(FrontendContext* fc) {
1960 size_t allScriptCount = scriptData.length();
1961 size_t nonLazyScriptCount = nonLazyFunctionCount;
1962 if (!scriptData[0].isFunction()) {
1963 nonLazyScriptCount++;
1965 return sharedData.prepareStorageFor(fc, nonLazyScriptCount, allScriptCount);
1968 static bool CreateLazyScript(JSContext* cx,
1969 const CompilationAtomCache& atomCache,
1970 const CompilationStencil& stencil,
1971 CompilationGCOutput& gcOutput,
1972 const ScriptStencil& script,
1973 const ScriptStencilExtra& scriptExtra,
1974 ScriptIndex scriptIndex, HandleFunction function) {
1975 Rooted<ScriptSourceObject*> sourceObject(cx, gcOutput.sourceObject);
1977 size_t ngcthings = script.gcThingsLength;
1979 Rooted<BaseScript*> lazy(
1980 cx, BaseScript::CreateRawLazy(cx, ngcthings, function, sourceObject,
1981 scriptExtra.extent,
1982 scriptExtra.immutableFlags));
1983 if (!lazy) {
1984 return false;
1987 if (ngcthings) {
1988 if (!EmitScriptThingsVector(cx, atomCache, stencil, gcOutput,
1989 script.gcthings(stencil),
1990 lazy->gcthingsForInit())) {
1991 return false;
1995 if (scriptExtra.useMemberInitializers()) {
1996 lazy->setMemberInitializers(scriptExtra.memberInitializers());
1999 function->initScript(lazy);
2001 return true;
2004 // Parser-generated functions with the same prototype will share the same shape.
2005 // By computing the correct values up front, we can save a lot of time in the
2006 // Object creation code. For simplicity, we focus only on plain synchronous
2007 // functions which are by far the most common.
2009 // NOTE: Keep this in sync with `js::NewFunctionWithProto`.
2010 static JSFunction* CreateFunctionFast(JSContext* cx,
2011 CompilationAtomCache& atomCache,
2012 Handle<SharedShape*> shape,
2013 const ScriptStencil& script,
2014 const ScriptStencilExtra& scriptExtra) {
2015 MOZ_ASSERT(
2016 !scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsAsync));
2017 MOZ_ASSERT(!scriptExtra.immutableFlags.hasFlag(
2018 ImmutableScriptFlagsEnum::IsGenerator));
2019 MOZ_ASSERT(!script.functionFlags.isAsmJSNative());
2021 FunctionFlags flags = script.functionFlags;
2022 gc::AllocKind allocKind = flags.isExtended()
2023 ? gc::AllocKind::FUNCTION_EXTENDED
2024 : gc::AllocKind::FUNCTION;
2026 JSFunction* fun = JSFunction::create(cx, allocKind, gc::Heap::Tenured, shape);
2027 if (!fun) {
2028 return nullptr;
2031 fun->setArgCount(scriptExtra.nargs);
2032 fun->setFlags(flags);
2034 fun->initScript(nullptr);
2035 fun->initEnvironment(nullptr);
2037 if (script.functionAtom) {
2038 JSAtom* atom = atomCache.getExistingAtomAt(cx, script.functionAtom);
2039 MOZ_ASSERT(atom);
2040 fun->initAtom(atom);
2043 #ifdef DEBUG
2044 fun->assertFunctionKindIntegrity();
2045 #endif
2047 return fun;
2050 static JSFunction* CreateFunction(JSContext* cx,
2051 CompilationAtomCache& atomCache,
2052 const CompilationStencil& stencil,
2053 const ScriptStencil& script,
2054 const ScriptStencilExtra& scriptExtra,
2055 ScriptIndex functionIndex) {
2056 GeneratorKind generatorKind =
2057 scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsGenerator)
2058 ? GeneratorKind::Generator
2059 : GeneratorKind::NotGenerator;
2060 FunctionAsyncKind asyncKind =
2061 scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsAsync)
2062 ? FunctionAsyncKind::AsyncFunction
2063 : FunctionAsyncKind::SyncFunction;
2065 // Determine the new function's proto. This must be done for singleton
2066 // functions.
2067 RootedObject proto(cx);
2068 if (!GetFunctionPrototype(cx, generatorKind, asyncKind, &proto)) {
2069 return nullptr;
2072 gc::AllocKind allocKind = script.functionFlags.isExtended()
2073 ? gc::AllocKind::FUNCTION_EXTENDED
2074 : gc::AllocKind::FUNCTION;
2075 bool isAsmJS = script.functionFlags.isAsmJSNative();
2077 JSNative maybeNative = isAsmJS ? InstantiateAsmJS : nullptr;
2079 Rooted<JSAtom*> displayAtom(cx);
2080 if (script.functionAtom) {
2081 displayAtom.set(atomCache.getExistingAtomAt(cx, script.functionAtom));
2082 MOZ_ASSERT(displayAtom);
2084 RootedFunction fun(
2085 cx, NewFunctionWithProto(cx, maybeNative, scriptExtra.nargs,
2086 script.functionFlags, nullptr, displayAtom,
2087 proto, allocKind, TenuredObject));
2088 if (!fun) {
2089 return nullptr;
2092 if (isAsmJS) {
2093 RefPtr<const JS::WasmModule> asmJS =
2094 stencil.asmJS->moduleMap.lookup(functionIndex)->value();
2096 JSObject* moduleObj = asmJS->createObjectForAsmJS(cx);
2097 if (!moduleObj) {
2098 return nullptr;
2101 fun->setExtendedSlot(FunctionExtended::ASMJS_MODULE_SLOT,
2102 ObjectValue(*moduleObj));
2105 return fun;
2108 static bool InstantiateAtoms(JSContext* cx, FrontendContext* fc,
2109 CompilationAtomCache& atomCache,
2110 const CompilationStencil& stencil) {
2111 return InstantiateMarkedAtoms(cx, fc, stencil.parserAtomData, atomCache);
2114 static bool InstantiateScriptSourceObject(JSContext* cx,
2115 const JS::InstantiateOptions& options,
2116 const CompilationStencil& stencil,
2117 CompilationGCOutput& gcOutput) {
2118 MOZ_ASSERT(stencil.source);
2120 gcOutput.sourceObject = ScriptSourceObject::create(cx, stencil.source.get());
2121 if (!gcOutput.sourceObject) {
2122 return false;
2125 Rooted<ScriptSourceObject*> sourceObject(cx, gcOutput.sourceObject);
2126 if (!ScriptSourceObject::initFromOptions(cx, sourceObject, options)) {
2127 return false;
2130 return true;
2133 // Instantiate ModuleObject. Further initialization is done after the associated
2134 // BaseScript is instantiated in InstantiateTopLevel.
2135 static bool InstantiateModuleObject(JSContext* cx, FrontendContext* fc,
2136 CompilationAtomCache& atomCache,
2137 const CompilationStencil& stencil,
2138 CompilationGCOutput& gcOutput) {
2139 MOZ_ASSERT(stencil.isModule());
2141 gcOutput.module = ModuleObject::create(cx);
2142 if (!gcOutput.module) {
2143 return false;
2146 Rooted<ModuleObject*> module(cx, gcOutput.module);
2147 return stencil.moduleMetadata->initModule(cx, fc, atomCache, module);
2150 // Instantiate JSFunctions for each FunctionBox.
2151 static bool InstantiateFunctions(JSContext* cx, FrontendContext* fc,
2152 CompilationAtomCache& atomCache,
2153 const CompilationStencil& stencil,
2154 CompilationGCOutput& gcOutput) {
2155 using ImmutableFlags = ImmutableScriptFlagsEnum;
2157 MOZ_ASSERT(gcOutput.functions.length() == stencil.scriptData.size());
2159 // Most JSFunctions will be have the same Shape so we can compute it now to
2160 // allow fast object creation. Generators / Async will use the slow path
2161 // instead.
2162 Rooted<SharedShape*> functionShape(
2163 cx, GlobalObject::getFunctionShapeWithDefaultProto(
2164 cx, /* extended = */ false));
2165 if (!functionShape) {
2166 return false;
2169 Rooted<SharedShape*> extendedShape(
2170 cx, GlobalObject::getFunctionShapeWithDefaultProto(
2171 cx, /* extended = */ true));
2172 if (!extendedShape) {
2173 return false;
2176 for (auto item :
2177 CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
2178 const auto& scriptStencil = item.script;
2179 const auto& scriptExtra = (*item.scriptExtra);
2180 auto index = item.index;
2182 MOZ_ASSERT(!item.function);
2184 // Plain functions can use a fast path.
2185 bool useFastPath =
2186 !scriptExtra.immutableFlags.hasFlag(ImmutableFlags::IsAsync) &&
2187 !scriptExtra.immutableFlags.hasFlag(ImmutableFlags::IsGenerator) &&
2188 !scriptStencil.functionFlags.isAsmJSNative();
2190 JSFunction* fun;
2191 if (useFastPath) {
2192 Handle<SharedShape*> shape = scriptStencil.functionFlags.isExtended()
2193 ? extendedShape
2194 : functionShape;
2195 fun =
2196 CreateFunctionFast(cx, atomCache, shape, scriptStencil, scriptExtra);
2197 } else {
2198 fun = CreateFunction(cx, atomCache, stencil, scriptStencil, scriptExtra,
2199 index);
2202 if (!fun) {
2203 return false;
2206 // Self-hosted functions may have a canonical name to use when instantiating
2207 // into other realms.
2208 if (scriptStencil.hasSelfHostedCanonicalName()) {
2209 JSAtom* canonicalName = atomCache.getExistingAtomAt(
2210 cx, scriptStencil.selfHostedCanonicalName());
2211 fun->setAtom(canonicalName);
2214 gcOutput.getFunctionNoBaseIndex(index) = fun;
2217 return true;
2220 // Instantiate Scope for each ScopeStencil.
2222 // This should be called after InstantiateFunctions, given FunctionScope needs
2223 // associated JSFunction pointer, and also should be called before
2224 // InstantiateScriptStencils, given JSScript needs Scope pointer in gc things.
2225 static bool InstantiateScopes(JSContext* cx, CompilationInput& input,
2226 const CompilationStencil& stencil,
2227 CompilationGCOutput& gcOutput) {
2228 // While allocating Scope object from ScopeStencil, Scope object for the
2229 // enclosing Scope should already be allocated.
2231 // Enclosing scope of ScopeStencil can be either ScopeStencil or Scope*
2232 // pointer.
2234 // If the enclosing scope is ScopeStencil, it's guaranteed to be earlier
2235 // element in stencil.scopeData, because enclosing_ field holds
2236 // index into it, and newly created ScopeStencil is pushed back to the array.
2238 // If the enclosing scope is Scope*, it's CompilationInput.enclosingScope.
2240 MOZ_ASSERT(stencil.scopeData.size() == stencil.scopeNames.size());
2241 size_t scopeCount = stencil.scopeData.size();
2242 for (size_t i = 0; i < scopeCount; i++) {
2243 Scope* scope = stencil.scopeData[i].createScope(cx, input, gcOutput,
2244 stencil.scopeNames[i]);
2245 if (!scope) {
2246 return false;
2248 gcOutput.scopes[i] = scope;
2251 return true;
2254 // Instantiate js::BaseScripts from ScriptStencils for inner functions of the
2255 // compilation. Note that standalone functions and functions being delazified
2256 // are handled below with other top-levels.
2257 static bool InstantiateScriptStencils(JSContext* cx,
2258 CompilationAtomCache& atomCache,
2259 const CompilationStencil& stencil,
2260 CompilationGCOutput& gcOutput) {
2261 MOZ_ASSERT(stencil.isInitialStencil());
2263 Rooted<JSFunction*> fun(cx);
2264 for (auto item :
2265 CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
2266 auto& scriptStencil = item.script;
2267 auto* scriptExtra = item.scriptExtra;
2268 fun = item.function;
2269 auto index = item.index;
2270 if (scriptStencil.hasSharedData()) {
2271 // If the function was not referenced by enclosing script's bytecode, we
2272 // do not generate a BaseScript for it. For example, `(function(){});`.
2274 // `wasEmittedByEnclosingScript` is false also for standalone
2275 // functions. They are handled in InstantiateTopLevel.
2276 if (!scriptStencil.wasEmittedByEnclosingScript()) {
2277 continue;
2280 RootedScript script(
2281 cx, JSScript::fromStencil(cx, atomCache, stencil, gcOutput, index));
2282 if (!script) {
2283 return false;
2286 if (scriptStencil.allowRelazify()) {
2287 MOZ_ASSERT(script->isRelazifiable());
2288 script->setAllowRelazify();
2290 } else if (scriptStencil.functionFlags.isAsmJSNative()) {
2291 MOZ_ASSERT(fun->isAsmJSNative());
2292 } else {
2293 MOZ_ASSERT(fun->isIncomplete());
2294 if (!CreateLazyScript(cx, atomCache, stencil, gcOutput, scriptStencil,
2295 *scriptExtra, index, fun)) {
2296 return false;
2301 return true;
2304 // Instantiate the Stencil for the top-level script of the compilation. This
2305 // includes standalone functions and functions being delazified.
2306 static bool InstantiateTopLevel(JSContext* cx, CompilationInput& input,
2307 const CompilationStencil& stencil,
2308 CompilationGCOutput& gcOutput) {
2309 const ScriptStencil& scriptStencil =
2310 stencil.scriptData[CompilationStencil::TopLevelIndex];
2312 // Top-level asm.js does not generate a JSScript.
2313 if (scriptStencil.functionFlags.isAsmJSNative()) {
2314 return true;
2317 MOZ_ASSERT(scriptStencil.hasSharedData());
2318 MOZ_ASSERT(stencil.sharedData.get(CompilationStencil::TopLevelIndex));
2320 if (!stencil.isInitialStencil()) {
2321 MOZ_ASSERT(input.lazyOuterBaseScript());
2322 RootedScript script(cx,
2323 JSScript::CastFromLazy(input.lazyOuterBaseScript()));
2324 if (!JSScript::fullyInitFromStencil(cx, input.atomCache, stencil, gcOutput,
2325 script,
2326 CompilationStencil::TopLevelIndex)) {
2327 return false;
2330 if (scriptStencil.allowRelazify()) {
2331 MOZ_ASSERT(script->isRelazifiable());
2332 script->setAllowRelazify();
2335 gcOutput.script = script;
2336 return true;
2339 gcOutput.script =
2340 JSScript::fromStencil(cx, input.atomCache, stencil, gcOutput,
2341 CompilationStencil::TopLevelIndex);
2342 if (!gcOutput.script) {
2343 return false;
2346 if (scriptStencil.allowRelazify()) {
2347 MOZ_ASSERT(gcOutput.script->isRelazifiable());
2348 gcOutput.script->setAllowRelazify();
2351 const ScriptStencilExtra& scriptExtra =
2352 stencil.scriptExtra[CompilationStencil::TopLevelIndex];
2354 // Finish initializing the ModuleObject if needed.
2355 if (scriptExtra.isModule()) {
2356 RootedScript script(cx, gcOutput.script);
2357 Rooted<ModuleObject*> module(cx, gcOutput.module);
2359 script->outermostScope()->as<ModuleScope>().initModule(module);
2361 module->initScriptSlots(script);
2363 if (!ModuleObject::createEnvironment(cx, module)) {
2364 return false;
2367 if (!ModuleObject::Freeze(cx, module)) {
2368 return false;
2372 return true;
2375 // When a function is first referenced by enclosing script's bytecode, we need
2376 // to update it with information determined by the BytecodeEmitter. This applies
2377 // to both initial and delazification parses. The functions being update may or
2378 // may not have bytecode at this point.
2379 static void UpdateEmittedInnerFunctions(JSContext* cx,
2380 CompilationAtomCache& atomCache,
2381 const CompilationStencil& stencil,
2382 CompilationGCOutput& gcOutput) {
2383 for (auto item :
2384 CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
2385 auto& scriptStencil = item.script;
2386 auto& fun = item.function;
2387 if (!scriptStencil.wasEmittedByEnclosingScript()) {
2388 continue;
2391 if (scriptStencil.functionFlags.isAsmJSNative() ||
2392 fun->baseScript()->hasBytecode()) {
2393 // Non-lazy inner functions don't use the enclosingScope_ field.
2394 MOZ_ASSERT(!scriptStencil.hasLazyFunctionEnclosingScopeIndex());
2395 } else {
2396 // Apply updates from FunctionEmitter::emitLazy().
2397 BaseScript* script = fun->baseScript();
2399 ScopeIndex index = scriptStencil.lazyFunctionEnclosingScopeIndex();
2400 Scope* scope = gcOutput.getScopeNoBaseIndex(index);
2401 script->setEnclosingScope(scope);
2403 // Inferred and Guessed names are computed by BytecodeEmitter and so may
2404 // need to be applied to existing JSFunctions during delazification.
2405 if (fun->fullDisplayAtom() == nullptr) {
2406 JSAtom* funcAtom = nullptr;
2407 if (scriptStencil.functionFlags.hasInferredName() ||
2408 scriptStencil.functionFlags.hasGuessedAtom()) {
2409 funcAtom =
2410 atomCache.getExistingAtomAt(cx, scriptStencil.functionAtom);
2411 MOZ_ASSERT(funcAtom);
2413 if (scriptStencil.functionFlags.hasInferredName()) {
2414 fun->setInferredName(funcAtom);
2416 if (scriptStencil.functionFlags.hasGuessedAtom()) {
2417 fun->setGuessedAtom(funcAtom);
2424 // During initial parse we must link lazy-functions-inside-lazy-functions to
2425 // their enclosing script.
2426 static void LinkEnclosingLazyScript(const CompilationStencil& stencil,
2427 CompilationGCOutput& gcOutput) {
2428 for (auto item :
2429 CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
2430 auto& scriptStencil = item.script;
2431 auto& fun = item.function;
2432 if (!scriptStencil.functionFlags.hasBaseScript()) {
2433 continue;
2436 if (!fun->baseScript()) {
2437 continue;
2440 if (fun->baseScript()->hasBytecode()) {
2441 continue;
2444 BaseScript* script = fun->baseScript();
2445 MOZ_ASSERT(!script->hasBytecode());
2447 for (auto inner : script->gcthings()) {
2448 if (!inner.is<JSObject>()) {
2449 continue;
2451 JSFunction* innerFun = &inner.as<JSObject>().as<JSFunction>();
2453 MOZ_ASSERT(innerFun->hasBaseScript(),
2454 "inner function should have base script");
2455 if (!innerFun->hasBaseScript()) {
2456 continue;
2459 // Check for the case that the inner function has the base script flag,
2460 // but still doesn't have the actual base script pointer.
2461 // `baseScript` method asserts the pointer itself, so no extra MOZ_ASSERT
2462 // here.
2463 if (!innerFun->baseScript()) {
2464 continue;
2467 innerFun->setEnclosingLazyScript(script);
2472 #ifdef DEBUG
2473 // Some fields aren't used in delazification, given the target functions and
2474 // scripts are already instantiated, but they still should match.
2475 static void AssertDelazificationFieldsMatch(const CompilationStencil& stencil,
2476 CompilationGCOutput& gcOutput) {
2477 for (auto item :
2478 CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
2479 auto& scriptStencil = item.script;
2480 auto* scriptExtra = item.scriptExtra;
2481 auto& fun = item.function;
2483 MOZ_ASSERT(scriptExtra == nullptr);
2485 // Names are updated by UpdateInnerFunctions.
2486 constexpr uint16_t HAS_INFERRED_NAME =
2487 uint16_t(FunctionFlags::Flags::HAS_INFERRED_NAME);
2488 constexpr uint16_t HAS_GUESSED_ATOM =
2489 uint16_t(FunctionFlags::Flags::HAS_GUESSED_ATOM);
2490 constexpr uint16_t MUTABLE_FLAGS =
2491 uint16_t(FunctionFlags::Flags::MUTABLE_FLAGS);
2492 constexpr uint16_t acceptableDifferenceForFunction =
2493 HAS_INFERRED_NAME | HAS_GUESSED_ATOM | MUTABLE_FLAGS;
2495 MOZ_ASSERT((fun->flags().toRaw() | acceptableDifferenceForFunction) ==
2496 (scriptStencil.functionFlags.toRaw() |
2497 acceptableDifferenceForFunction));
2499 // Delazification shouldn't delazify inner scripts.
2500 MOZ_ASSERT_IF(item.index == CompilationStencil::TopLevelIndex,
2501 scriptStencil.hasSharedData());
2502 MOZ_ASSERT_IF(item.index > CompilationStencil::TopLevelIndex,
2503 !scriptStencil.hasSharedData());
2506 #endif // DEBUG
2508 // When delazifying, use the existing JSFunctions. The initial and delazifying
2509 // parse are required to generate the same sequence of functions for lazy
2510 // parsing to work at all.
2511 static void FunctionsFromExistingLazy(CompilationInput& input,
2512 CompilationGCOutput& gcOutput) {
2513 MOZ_ASSERT(!gcOutput.functions[0]);
2515 size_t instantiatedFunIndex = 0;
2516 gcOutput.functions[instantiatedFunIndex++] = input.function();
2518 for (JS::GCCellPtr elem : input.lazyOuterBaseScript()->gcthings()) {
2519 if (!elem.is<JSObject>()) {
2520 continue;
2522 JSFunction* fun = &elem.as<JSObject>().as<JSFunction>();
2523 gcOutput.functions[instantiatedFunIndex++] = fun;
2527 void CompilationStencil::borrowFromExtensibleCompilationStencil(
2528 ExtensibleCompilationStencil& extensibleStencil) {
2529 canLazilyParse = extensibleStencil.canLazilyParse;
2530 functionKey = extensibleStencil.functionKey;
2532 // Borrow the vector content as span.
2533 scriptData = extensibleStencil.scriptData;
2534 scriptExtra = extensibleStencil.scriptExtra;
2536 gcThingData = extensibleStencil.gcThingData;
2538 scopeData = extensibleStencil.scopeData;
2539 scopeNames = extensibleStencil.scopeNames;
2541 regExpData = extensibleStencil.regExpData;
2542 bigIntData = extensibleStencil.bigIntData;
2543 objLiteralData = extensibleStencil.objLiteralData;
2545 // Borrow the parser atoms as span.
2546 parserAtomData = extensibleStencil.parserAtoms.entries_;
2548 // Borrow container.
2549 sharedData.setBorrow(&extensibleStencil.sharedData);
2551 // Share ref-counted data.
2552 source = extensibleStencil.source;
2553 asmJS = extensibleStencil.asmJS;
2554 moduleMetadata = extensibleStencil.moduleMetadata;
2557 #ifdef DEBUG
2558 void CompilationStencil::assertBorrowingFromExtensibleCompilationStencil(
2559 const ExtensibleCompilationStencil& extensibleStencil) const {
2560 MOZ_ASSERT(canLazilyParse == extensibleStencil.canLazilyParse);
2561 MOZ_ASSERT(functionKey == extensibleStencil.functionKey);
2563 AssertBorrowingSpan(scriptData, extensibleStencil.scriptData);
2564 AssertBorrowingSpan(scriptExtra, extensibleStencil.scriptExtra);
2566 AssertBorrowingSpan(gcThingData, extensibleStencil.gcThingData);
2568 AssertBorrowingSpan(scopeData, extensibleStencil.scopeData);
2569 AssertBorrowingSpan(scopeNames, extensibleStencil.scopeNames);
2571 AssertBorrowingSpan(regExpData, extensibleStencil.regExpData);
2572 AssertBorrowingSpan(bigIntData, extensibleStencil.bigIntData);
2573 AssertBorrowingSpan(objLiteralData, extensibleStencil.objLiteralData);
2575 AssertBorrowingSpan(parserAtomData, extensibleStencil.parserAtoms.entries_);
2577 MOZ_ASSERT(sharedData.isBorrow());
2578 MOZ_ASSERT(sharedData.asBorrow() == &extensibleStencil.sharedData);
2580 MOZ_ASSERT(source == extensibleStencil.source);
2581 MOZ_ASSERT(asmJS == extensibleStencil.asmJS);
2582 MOZ_ASSERT(moduleMetadata == extensibleStencil.moduleMetadata);
2584 #endif
2586 CompilationStencil::CompilationStencil(
2587 UniquePtr<ExtensibleCompilationStencil>&& extensibleStencil)
2588 : alloc(LifoAllocChunkSize) {
2589 ownedBorrowStencil = std::move(extensibleStencil);
2591 storageType = StorageType::OwnedExtensible;
2593 borrowFromExtensibleCompilationStencil(*ownedBorrowStencil);
2595 #ifdef DEBUG
2596 assertNoExternalDependency();
2597 #endif
2600 /* static */
2601 bool CompilationStencil::instantiateStencils(JSContext* cx,
2602 CompilationInput& input,
2603 const CompilationStencil& stencil,
2604 CompilationGCOutput& gcOutput) {
2605 AutoReportFrontendContext fc(cx);
2606 if (!prepareForInstantiate(&fc, input.atomCache, stencil, gcOutput)) {
2607 return false;
2610 return instantiateStencilAfterPreparation(cx, input, stencil, gcOutput);
2613 /* static */
2614 bool CompilationStencil::instantiateStencilAfterPreparation(
2615 JSContext* cx, CompilationInput& input, const CompilationStencil& stencil,
2616 CompilationGCOutput& gcOutput) {
2617 // Distinguish between the initial (possibly lazy) compile and any subsequent
2618 // delazification compiles. Delazification will update existing GC things.
2619 bool isInitialParse = stencil.isInitialStencil();
2620 MOZ_ASSERT(stencil.isInitialStencil() == input.isInitialStencil());
2622 CompilationAtomCache& atomCache = input.atomCache;
2623 const JS::InstantiateOptions options(input.options);
2625 // Phase 1: Instantiate JSAtom/JSStrings.
2626 AutoReportFrontendContext fc(cx);
2627 if (!InstantiateAtoms(cx, &fc, atomCache, stencil)) {
2628 return false;
2631 // Phase 2: Instantiate ScriptSourceObject, ModuleObject, JSFunctions.
2632 if (isInitialParse) {
2633 if (!InstantiateScriptSourceObject(cx, options, stencil, gcOutput)) {
2634 return false;
2637 if (stencil.moduleMetadata) {
2638 // The enclosing script of a module is always the global scope. Fetch the
2639 // scope of the current global and update input data.
2640 MOZ_ASSERT(input.enclosingScope.isNull());
2641 input.enclosingScope = InputScope(&cx->global()->emptyGlobalScope());
2642 MOZ_ASSERT(input.enclosingScope.environmentChainLength() ==
2643 ModuleScope::EnclosingEnvironmentChainLength);
2645 if (!InstantiateModuleObject(cx, &fc, atomCache, stencil, gcOutput)) {
2646 return false;
2650 if (!InstantiateFunctions(cx, &fc, atomCache, stencil, gcOutput)) {
2651 return false;
2653 } else {
2654 MOZ_ASSERT(
2655 stencil.scriptData[CompilationStencil::TopLevelIndex].isFunction());
2657 // FunctionKey is used when caching to map a delazification stencil to a
2658 // specific lazy script. It is not used by instantiation, but we should
2659 // ensure it is correctly defined.
2660 MOZ_ASSERT(stencil.functionKey == input.extent().toFunctionKey());
2662 FunctionsFromExistingLazy(input, gcOutput);
2663 MOZ_ASSERT(gcOutput.functions.length() == stencil.scriptData.size());
2665 #ifdef DEBUG
2666 AssertDelazificationFieldsMatch(stencil, gcOutput);
2667 #endif
2670 // Phase 3: Instantiate js::Scopes.
2671 if (!InstantiateScopes(cx, input, stencil, gcOutput)) {
2672 return false;
2675 // Phase 4: Instantiate (inner) BaseScripts.
2676 if (isInitialParse) {
2677 if (!InstantiateScriptStencils(cx, atomCache, stencil, gcOutput)) {
2678 return false;
2682 // Phase 5: Finish top-level handling
2683 if (!InstantiateTopLevel(cx, input, stencil, gcOutput)) {
2684 return false;
2687 // !! Must be infallible from here forward !!
2689 // Phase 6: Update lazy scripts.
2690 if (stencil.canLazilyParse) {
2691 UpdateEmittedInnerFunctions(cx, atomCache, stencil, gcOutput);
2693 if (isInitialParse) {
2694 LinkEnclosingLazyScript(stencil, gcOutput);
2698 return true;
2701 // The top-level self-hosted script is created and executed in each realm that
2702 // needs it. While the stencil has a gcthings list for the various top-level
2703 // functions, we use special machinery to create them on demand. So instead we
2704 // use a placeholder JSFunction that should never be called.
2705 static bool SelfHostedDummyFunction(JSContext* cx, unsigned argc,
2706 JS::Value* vp) {
2707 MOZ_CRASH("Self-hosting top-level should not use functions directly");
2710 bool CompilationStencil::instantiateSelfHostedAtoms(
2711 JSContext* cx, AtomSet& atomSet, CompilationAtomCache& atomCache) const {
2712 MOZ_ASSERT(isInitialStencil());
2714 // We must instantiate atoms during startup so they can be made permanent
2715 // across multiple runtimes.
2716 AutoReportFrontendContext fc(cx);
2717 return InstantiateMarkedAtomsAsPermanent(cx, &fc, atomSet, parserAtomData,
2718 atomCache);
2721 JSScript* CompilationStencil::instantiateSelfHostedTopLevelForRealm(
2722 JSContext* cx, CompilationInput& input) {
2723 MOZ_ASSERT(isInitialStencil());
2725 Rooted<CompilationGCOutput> gcOutput(cx);
2727 gcOutput.get().sourceObject = SelfHostingScriptSourceObject(cx);
2728 if (!gcOutput.get().sourceObject) {
2729 return nullptr;
2732 // The top-level script has ScriptIndex references in its gcthings list, but
2733 // we do not want to instantiate those functions here since they are instead
2734 // created on demand from the stencil. Create a dummy function and populate
2735 // the functions array of the CompilationGCOutput with references to it.
2736 RootedFunction dummy(
2737 cx, NewNativeFunction(cx, SelfHostedDummyFunction, 0, nullptr));
2738 if (!dummy) {
2739 return nullptr;
2742 if (!gcOutput.get().functions.allocateWith(dummy, scriptData.size())) {
2743 ReportOutOfMemory(cx);
2744 return nullptr;
2747 if (!InstantiateTopLevel(cx, input, *this, gcOutput.get())) {
2748 return nullptr;
2751 return gcOutput.get().script;
2754 JSFunction* CompilationStencil::instantiateSelfHostedLazyFunction(
2755 JSContext* cx, CompilationAtomCache& atomCache, ScriptIndex index,
2756 Handle<JSAtom*> name) {
2757 GeneratorKind generatorKind = scriptExtra[index].immutableFlags.hasFlag(
2758 ImmutableScriptFlagsEnum::IsGenerator)
2759 ? GeneratorKind::Generator
2760 : GeneratorKind::NotGenerator;
2761 FunctionAsyncKind asyncKind = scriptExtra[index].immutableFlags.hasFlag(
2762 ImmutableScriptFlagsEnum::IsAsync)
2763 ? FunctionAsyncKind::AsyncFunction
2764 : FunctionAsyncKind::SyncFunction;
2766 Rooted<JSAtom*> funName(cx);
2767 if (scriptData[index].hasSelfHostedCanonicalName()) {
2768 // SetCanonicalName was used to override the name.
2769 funName = atomCache.getExistingAtomAt(
2770 cx, scriptData[index].selfHostedCanonicalName());
2771 } else if (name) {
2772 // Our caller has a name it wants to use.
2773 funName = name;
2774 } else {
2775 MOZ_ASSERT(scriptData[index].functionAtom);
2776 funName = atomCache.getExistingAtomAt(cx, scriptData[index].functionAtom);
2779 RootedObject proto(cx);
2780 if (!GetFunctionPrototype(cx, generatorKind, asyncKind, &proto)) {
2781 return nullptr;
2784 RootedObject env(cx, &cx->global()->lexicalEnvironment());
2786 RootedFunction fun(
2788 NewFunctionWithProto(cx, nullptr, scriptExtra[index].nargs,
2789 scriptData[index].functionFlags, env, funName, proto,
2790 gc::AllocKind::FUNCTION_EXTENDED, TenuredObject));
2791 if (!fun) {
2792 return nullptr;
2795 fun->initSelfHostedLazyScript(&cx->runtime()->selfHostedLazyScript.ref());
2797 JSAtom* selfHostedName =
2798 atomCache.getExistingAtomAt(cx, scriptData[index].functionAtom);
2799 SetClonedSelfHostedFunctionName(fun, selfHostedName->asPropertyName());
2801 return fun;
2804 bool CompilationStencil::delazifySelfHostedFunction(
2805 JSContext* cx, CompilationAtomCache& atomCache, ScriptIndexRange range,
2806 HandleFunction fun) {
2807 // Determine the equivalent ScopeIndex range by looking at the outermost scope
2808 // of the scripts defining the range. Take special care if this is the last
2809 // script in the list.
2810 auto getOutermostScope = [this](ScriptIndex scriptIndex) -> ScopeIndex {
2811 MOZ_ASSERT(scriptData[scriptIndex].hasSharedData());
2812 auto gcthings = scriptData[scriptIndex].gcthings(*this);
2813 return gcthings[GCThingIndex::outermostScopeIndex()].toScope();
2815 ScopeIndex scopeIndex = getOutermostScope(range.start);
2816 ScopeIndex scopeLimit = (range.limit < scriptData.size())
2817 ? getOutermostScope(range.limit)
2818 : ScopeIndex(scopeData.size());
2820 // Prepare to instantiate by allocating the output arrays. We also set a base
2821 // index to avoid allocations in most cases.
2822 AutoReportFrontendContext fc(cx);
2823 Rooted<CompilationGCOutput> gcOutput(cx);
2824 if (!gcOutput.get().ensureAllocatedWithBaseIndex(
2825 &fc, range.start, range.limit, scopeIndex, scopeLimit)) {
2826 return false;
2829 // Phase 1: Instantiate JSAtoms.
2830 // NOTE: The self-hosted atoms are all "permanent" and the
2831 // CompilationAtomCache is already stored on the JSRuntime.
2833 // Phase 2: Instantiate ScriptSourceObject, ModuleObject, JSFunctions.
2835 // Get the corresponding ScriptSourceObject to use in current realm.
2836 gcOutput.get().sourceObject = SelfHostingScriptSourceObject(cx);
2837 if (!gcOutput.get().sourceObject) {
2838 return false;
2841 size_t instantiatedFunIndex = 0;
2843 // Delazification target function.
2844 gcOutput.get().functions[instantiatedFunIndex++] = fun;
2846 // Allocate inner functions. Self-hosted functions do not allocate these with
2847 // the initial function.
2848 for (size_t i = range.start + 1; i < range.limit; i++) {
2849 JSFunction* innerFun = CreateFunction(cx, atomCache, *this, scriptData[i],
2850 scriptExtra[i], ScriptIndex(i));
2851 if (!innerFun) {
2852 return false;
2854 gcOutput.get().functions[instantiatedFunIndex++] = innerFun;
2857 // Phase 3: Instantiate js::Scopes.
2858 // NOTE: When the enclosing scope is not a stencil, directly use the
2859 // `emptyGlobalScope` instead of reading from CompilationInput. This is
2860 // a special case for self-hosted delazification that allows us to reuse
2861 // the CompilationInput between different realms.
2862 size_t instantiatedScopeIndex = 0;
2863 for (size_t i = scopeIndex; i < scopeLimit; i++) {
2864 ScopeStencil& data = scopeData[i];
2865 Rooted<Scope*> enclosingScope(
2866 cx, data.hasEnclosing() ? gcOutput.get().getScope(data.enclosing())
2867 : &cx->global()->emptyGlobalScope());
2869 js::Scope* scope =
2870 data.createScope(cx, atomCache, enclosingScope, scopeNames[i]);
2871 if (!scope) {
2872 return false;
2874 gcOutput.get().scopes[instantiatedScopeIndex++] = scope;
2877 // Phase 4: Instantiate (inner) BaseScripts.
2878 ScriptIndex innerStart(range.start + 1);
2879 for (size_t i = innerStart; i < range.limit; i++) {
2880 if (!JSScript::fromStencil(cx, atomCache, *this, gcOutput.get(),
2881 ScriptIndex(i))) {
2882 return false;
2886 // Phase 5: Finish top-level handling
2887 // NOTE: We do not have a `CompilationInput` handy here, so avoid using the
2888 // `InstantiateTopLevel` helper and directly create the JSScript. Our
2889 // caller also handles the `AllowRelazify` flag for us since self-hosted
2890 // delazification is a special case.
2891 if (!JSScript::fromStencil(cx, atomCache, *this, gcOutput.get(),
2892 range.start)) {
2893 return false;
2896 // Phase 6: Update lazy scripts.
2897 // NOTE: Self-hosting is always fully parsed so there is nothing to do here.
2899 return true;
2902 /* static */
2903 bool CompilationStencil::prepareForInstantiate(
2904 FrontendContext* fc, CompilationAtomCache& atomCache,
2905 const CompilationStencil& stencil, CompilationGCOutput& gcOutput) {
2906 // Allocate the `gcOutput` arrays.
2907 if (!gcOutput.ensureAllocated(fc, stencil.scriptData.size(),
2908 stencil.scopeData.size())) {
2909 return false;
2912 return atomCache.allocate(fc, stencil.parserAtomData.size());
2915 /* static */
2916 bool CompilationStencil::prepareForInstantiate(
2917 FrontendContext* fc, const CompilationStencil& stencil,
2918 PreallocatedCompilationGCOutput& gcOutput) {
2919 return gcOutput.allocate(fc, stencil.scriptData.size(),
2920 stencil.scopeData.size());
2923 bool CompilationStencil::serializeStencils(JSContext* cx,
2924 CompilationInput& input,
2925 JS::TranscodeBuffer& buf,
2926 bool* succeededOut) const {
2927 if (succeededOut) {
2928 *succeededOut = false;
2930 AutoReportFrontendContext fc(cx);
2931 XDRStencilEncoder encoder(&fc, buf);
2933 XDRResult res = encoder.codeStencil(*this);
2934 if (res.isErr()) {
2935 if (JS::IsTranscodeFailureResult(res.unwrapErr())) {
2936 buf.clear();
2937 return true;
2939 MOZ_ASSERT(res.unwrapErr() == JS::TranscodeResult::Throw);
2941 return false;
2944 if (succeededOut) {
2945 *succeededOut = true;
2947 return true;
2950 bool CompilationStencil::deserializeStencils(
2951 FrontendContext* fc, const JS::ReadOnlyCompileOptions& compileOptions,
2952 const JS::TranscodeRange& range, bool* succeededOut) {
2953 if (succeededOut) {
2954 *succeededOut = false;
2956 MOZ_ASSERT(parserAtomData.empty());
2957 XDRStencilDecoder decoder(fc, range);
2958 JS::DecodeOptions options(compileOptions);
2960 XDRResult res = decoder.codeStencil(options, *this);
2961 if (res.isErr()) {
2962 if (JS::IsTranscodeFailureResult(res.unwrapErr())) {
2963 return true;
2965 MOZ_ASSERT(res.unwrapErr() == JS::TranscodeResult::Throw);
2967 return false;
2970 if (succeededOut) {
2971 *succeededOut = true;
2973 return true;
2976 ExtensibleCompilationStencil::ExtensibleCompilationStencil(ScriptSource* source)
2977 : alloc(CompilationStencil::LifoAllocChunkSize),
2978 source(source),
2979 parserAtoms(alloc) {}
2981 ExtensibleCompilationStencil::ExtensibleCompilationStencil(
2982 CompilationInput& input)
2983 : canLazilyParse(CanLazilyParse(input.options)),
2984 alloc(CompilationStencil::LifoAllocChunkSize),
2985 source(input.source),
2986 parserAtoms(alloc) {}
2988 ExtensibleCompilationStencil::ExtensibleCompilationStencil(
2989 const JS::ReadOnlyCompileOptions& options, RefPtr<ScriptSource> source)
2990 : canLazilyParse(CanLazilyParse(options)),
2991 alloc(CompilationStencil::LifoAllocChunkSize),
2992 source(std::move(source)),
2993 parserAtoms(alloc) {}
2995 CompilationState::CompilationState(FrontendContext* fc,
2996 LifoAllocScope& parserAllocScope,
2997 CompilationInput& input)
2998 : ExtensibleCompilationStencil(input),
2999 directives(input.options.forceStrictMode()),
3000 usedNames(fc),
3001 parserAllocScope(parserAllocScope),
3002 input(input) {}
3004 BorrowingCompilationStencil::BorrowingCompilationStencil(
3005 ExtensibleCompilationStencil& extensibleStencil)
3006 : CompilationStencil(extensibleStencil.source) {
3007 storageType = StorageType::Borrowed;
3009 borrowFromExtensibleCompilationStencil(extensibleStencil);
3012 SharedDataContainer::~SharedDataContainer() {
3013 if (isEmpty()) {
3014 // Nothing to do.
3015 } else if (isSingle()) {
3016 asSingle()->Release();
3017 } else if (isVector()) {
3018 js_delete(asVector());
3019 } else if (isMap()) {
3020 js_delete(asMap());
3021 } else {
3022 MOZ_ASSERT(isBorrow());
3023 // Nothing to do.
3027 bool SharedDataContainer::initVector(FrontendContext* fc) {
3028 MOZ_ASSERT(isEmpty());
3030 auto* vec = js_new<SharedDataVector>();
3031 if (!vec) {
3032 ReportOutOfMemory(fc);
3033 return false;
3035 data_ = uintptr_t(vec) | VectorTag;
3036 return true;
3039 bool SharedDataContainer::initMap(FrontendContext* fc) {
3040 MOZ_ASSERT(isEmpty());
3042 auto* map = js_new<SharedDataMap>();
3043 if (!map) {
3044 ReportOutOfMemory(fc);
3045 return false;
3047 data_ = uintptr_t(map) | MapTag;
3048 return true;
3051 bool SharedDataContainer::prepareStorageFor(FrontendContext* fc,
3052 size_t nonLazyScriptCount,
3053 size_t allScriptCount) {
3054 MOZ_ASSERT(isEmpty());
3056 if (nonLazyScriptCount <= 1) {
3057 MOZ_ASSERT(isSingle());
3058 return true;
3061 // If the ratio of scripts with bytecode is small, allocating the Vector
3062 // storage with the number of all scripts isn't space-efficient.
3063 // In that case use HashMap instead.
3065 // In general, we expect either all scripts to contain bytecode (priviledge
3066 // and self-hosted), or almost none to (eg standard lazy parsing output).
3067 constexpr size_t thresholdRatio = 8;
3068 bool useHashMap = nonLazyScriptCount < allScriptCount / thresholdRatio;
3069 if (useHashMap) {
3070 if (!initMap(fc)) {
3071 return false;
3073 if (!asMap()->reserve(nonLazyScriptCount)) {
3074 ReportOutOfMemory(fc);
3075 return false;
3077 } else {
3078 if (!initVector(fc)) {
3079 return false;
3081 if (!asVector()->resize(allScriptCount)) {
3082 ReportOutOfMemory(fc);
3083 return false;
3087 return true;
3090 bool SharedDataContainer::cloneFrom(FrontendContext* fc,
3091 const SharedDataContainer& other) {
3092 MOZ_ASSERT(isEmpty());
3094 if (other.isBorrow()) {
3095 return cloneFrom(fc, *other.asBorrow());
3098 if (other.isSingle()) {
3099 // As we clone, we add an extra reference.
3100 RefPtr<SharedImmutableScriptData> ref(other.asSingle());
3101 setSingle(ref.forget());
3102 } else if (other.isVector()) {
3103 if (!initVector(fc)) {
3104 return false;
3106 if (!asVector()->appendAll(*other.asVector())) {
3107 ReportOutOfMemory(fc);
3108 return false;
3110 } else if (other.isMap()) {
3111 if (!initMap(fc)) {
3112 return false;
3114 auto& otherMap = *other.asMap();
3115 if (!asMap()->reserve(otherMap.count())) {
3116 ReportOutOfMemory(fc);
3117 return false;
3119 auto& map = *asMap();
3120 for (auto iter = otherMap.iter(); !iter.done(); iter.next()) {
3121 auto& entry = iter.get();
3122 map.putNewInfallible(entry.key(), entry.value());
3125 return true;
3128 js::SharedImmutableScriptData* SharedDataContainer::get(
3129 ScriptIndex index) const {
3130 if (isSingle()) {
3131 if (index == CompilationStencil::TopLevelIndex) {
3132 return asSingle();
3134 return nullptr;
3137 if (isVector()) {
3138 auto& vec = *asVector();
3139 if (index.index < vec.length()) {
3140 return vec[index];
3142 return nullptr;
3145 if (isMap()) {
3146 auto& map = *asMap();
3147 auto p = map.lookup(index);
3148 if (p) {
3149 return p->value();
3151 return nullptr;
3154 MOZ_ASSERT(isBorrow());
3155 return asBorrow()->get(index);
3158 bool SharedDataContainer::convertFromSingleToMap(FrontendContext* fc) {
3159 MOZ_ASSERT(isSingle());
3161 // Use a temporary container so that on OOM we do not break the stencil.
3162 SharedDataContainer other;
3163 if (!other.initMap(fc)) {
3164 return false;
3167 if (!other.asMap()->putNew(CompilationStencil::TopLevelIndex, asSingle())) {
3168 ReportOutOfMemory(fc);
3169 return false;
3172 std::swap(data_, other.data_);
3173 return true;
3176 bool SharedDataContainer::addAndShare(FrontendContext* fc, ScriptIndex index,
3177 js::SharedImmutableScriptData* data) {
3178 MOZ_ASSERT(!isBorrow());
3180 if (isSingle()) {
3181 MOZ_ASSERT(index == CompilationStencil::TopLevelIndex);
3182 RefPtr<SharedImmutableScriptData> ref(data);
3183 if (!SharedImmutableScriptData::shareScriptData(fc, ref)) {
3184 return false;
3186 setSingle(ref.forget());
3187 return true;
3190 if (isVector()) {
3191 auto& vec = *asVector();
3192 // Resized by SharedDataContainer::prepareStorageFor.
3193 vec[index] = data;
3194 return SharedImmutableScriptData::shareScriptData(fc, vec[index]);
3197 MOZ_ASSERT(isMap());
3198 auto& map = *asMap();
3199 // Reserved by SharedDataContainer::prepareStorageFor.
3200 map.putNewInfallible(index, data);
3201 auto p = map.lookup(index);
3202 MOZ_ASSERT(p);
3203 return SharedImmutableScriptData::shareScriptData(fc, p->value());
3206 bool SharedDataContainer::addExtraWithoutShare(
3207 FrontendContext* fc, ScriptIndex index,
3208 js::SharedImmutableScriptData* data) {
3209 MOZ_ASSERT(!isEmpty());
3211 if (isSingle()) {
3212 if (!convertFromSingleToMap(fc)) {
3213 return false;
3217 if (isVector()) {
3218 // SharedDataContainer::prepareStorageFor allocates space for all scripts.
3219 (*asVector())[index] = data;
3220 return true;
3223 MOZ_ASSERT(isMap());
3224 // SharedDataContainer::prepareStorageFor doesn't allocate space for
3225 // delazification, and this can fail.
3226 if (!asMap()->putNew(index, data)) {
3227 ReportOutOfMemory(fc);
3228 return false;
3230 return true;
3233 #ifdef DEBUG
3234 void CompilationStencil::assertNoExternalDependency() const {
3235 if (ownedBorrowStencil) {
3236 ownedBorrowStencil->assertNoExternalDependency();
3238 assertBorrowingFromExtensibleCompilationStencil(*ownedBorrowStencil);
3239 return;
3242 MOZ_ASSERT_IF(!scriptData.empty(), alloc.contains(scriptData.data()));
3243 MOZ_ASSERT_IF(!scriptExtra.empty(), alloc.contains(scriptExtra.data()));
3245 MOZ_ASSERT_IF(!scopeData.empty(), alloc.contains(scopeData.data()));
3246 MOZ_ASSERT_IF(!scopeNames.empty(), alloc.contains(scopeNames.data()));
3247 for (const auto* data : scopeNames) {
3248 MOZ_ASSERT_IF(data, alloc.contains(data));
3251 MOZ_ASSERT_IF(!regExpData.empty(), alloc.contains(regExpData.data()));
3253 MOZ_ASSERT_IF(!bigIntData.empty(), alloc.contains(bigIntData.data()));
3254 for (const auto& data : bigIntData) {
3255 MOZ_ASSERT(data.isContainedIn(alloc));
3258 MOZ_ASSERT_IF(!objLiteralData.empty(), alloc.contains(objLiteralData.data()));
3259 for (const auto& data : objLiteralData) {
3260 MOZ_ASSERT(data.isContainedIn(alloc));
3263 MOZ_ASSERT_IF(!parserAtomData.empty(), alloc.contains(parserAtomData.data()));
3264 for (const auto* data : parserAtomData) {
3265 MOZ_ASSERT_IF(data, alloc.contains(data));
3268 MOZ_ASSERT(!sharedData.isBorrow());
3271 void ExtensibleCompilationStencil::assertNoExternalDependency() const {
3272 for (const auto& data : bigIntData) {
3273 MOZ_ASSERT(data.isContainedIn(alloc));
3276 for (const auto& data : objLiteralData) {
3277 MOZ_ASSERT(data.isContainedIn(alloc));
3280 for (const auto* data : scopeNames) {
3281 MOZ_ASSERT_IF(data, alloc.contains(data));
3284 for (const auto* data : parserAtoms.entries()) {
3285 MOZ_ASSERT_IF(data, alloc.contains(data));
3288 MOZ_ASSERT(!sharedData.isBorrow());
3290 #endif // DEBUG
3292 template <typename T, typename VectorT>
3293 [[nodiscard]] bool CopySpanToVector(FrontendContext* fc, VectorT& vec,
3294 mozilla::Span<T>& span) {
3295 auto len = span.size();
3296 if (len == 0) {
3297 return true;
3300 if (!vec.append(span.data(), len)) {
3301 js::ReportOutOfMemory(fc);
3302 return false;
3304 return true;
3307 template <typename T, typename IntoSpanT, size_t Inline, typename AllocPolicy>
3308 [[nodiscard]] bool CopyToVector(FrontendContext* fc,
3309 mozilla::Vector<T, Inline, AllocPolicy>& vec,
3310 const IntoSpanT& source) {
3311 mozilla::Span<const T> span = source;
3312 return CopySpanToVector(fc, vec, span);
3315 // Span and Vector do not share the same method names.
3316 template <typename T, size_t Inline, typename AllocPolicy>
3317 size_t GetLength(const mozilla::Vector<T, Inline, AllocPolicy>& vec) {
3318 return vec.length();
3320 template <typename T>
3321 size_t GetLength(const mozilla::Span<T>& span) {
3322 return span.Length();
3325 // Copy scope names from `src` into `alloc`, and returns the allocated data.
3326 BaseParserScopeData* CopyScopeData(FrontendContext* fc, LifoAlloc& alloc,
3327 ScopeKind kind,
3328 const BaseParserScopeData* src) {
3329 MOZ_ASSERT(kind != ScopeKind::With);
3331 size_t dataSize = SizeOfParserScopeData(kind, src->length);
3333 auto* dest = static_cast<BaseParserScopeData*>(alloc.alloc(dataSize));
3334 if (!dest) {
3335 js::ReportOutOfMemory(fc);
3336 return nullptr;
3338 memcpy(dest, src, dataSize);
3340 return dest;
3343 template <typename Stencil>
3344 bool ExtensibleCompilationStencil::cloneFromImpl(FrontendContext* fc,
3345 const Stencil& other) {
3346 MOZ_ASSERT(alloc.isEmpty());
3348 canLazilyParse = other.canLazilyParse;
3349 functionKey = other.functionKey;
3351 if (!CopyToVector(fc, scriptData, other.scriptData)) {
3352 return false;
3355 if (!CopyToVector(fc, scriptExtra, other.scriptExtra)) {
3356 return false;
3359 if (!CopyToVector(fc, gcThingData, other.gcThingData)) {
3360 return false;
3363 size_t scopeSize = GetLength(other.scopeData);
3364 if (!CopyToVector(fc, scopeData, other.scopeData)) {
3365 return false;
3367 if (!scopeNames.reserve(scopeSize)) {
3368 js::ReportOutOfMemory(fc);
3369 return false;
3371 for (size_t i = 0; i < scopeSize; i++) {
3372 if (other.scopeNames[i]) {
3373 BaseParserScopeData* data = CopyScopeData(
3374 fc, alloc, other.scopeData[i].kind(), other.scopeNames[i]);
3375 if (!data) {
3376 return false;
3378 scopeNames.infallibleEmplaceBack(data);
3379 } else {
3380 scopeNames.infallibleEmplaceBack(nullptr);
3384 if (!CopyToVector(fc, regExpData, other.regExpData)) {
3385 return false;
3388 // If CompilationStencil has external dependency, peform deep copy.
3390 size_t bigIntSize = GetLength(other.bigIntData);
3391 if (!bigIntData.resize(bigIntSize)) {
3392 js::ReportOutOfMemory(fc);
3393 return false;
3395 for (size_t i = 0; i < bigIntSize; i++) {
3396 if (!bigIntData[i].init(fc, alloc, other.bigIntData[i].source())) {
3397 return false;
3401 size_t objLiteralSize = GetLength(other.objLiteralData);
3402 if (!objLiteralData.reserve(objLiteralSize)) {
3403 js::ReportOutOfMemory(fc);
3404 return false;
3406 for (const auto& data : other.objLiteralData) {
3407 size_t length = data.code().size();
3408 auto* code = alloc.newArrayUninitialized<uint8_t>(length);
3409 if (!code) {
3410 js::ReportOutOfMemory(fc);
3411 return false;
3413 memcpy(code, data.code().data(), length);
3414 objLiteralData.infallibleEmplaceBack(code, length, data.kind(),
3415 data.flags(), data.propertyCount());
3418 // Regardless of whether CompilationStencil has external dependency or not,
3419 // ParserAtoms should be interned, to populate internal HashMap.
3420 for (const auto* entry : other.parserAtomsSpan()) {
3421 if (!entry) {
3422 if (!parserAtoms.addPlaceholder(fc)) {
3423 return false;
3425 continue;
3428 auto index = parserAtoms.internExternalParserAtom(fc, entry);
3429 if (!index) {
3430 return false;
3434 // We copy the stencil and increment the reference count of each
3435 // SharedImmutableScriptData.
3436 if (!sharedData.cloneFrom(fc, other.sharedData)) {
3437 return false;
3440 // Note: moduleMetadata and asmJS are known after the first parse, and are
3441 // not mutated by any delazifications later on. Thus we can safely increment
3442 // the reference counter and keep these as-is.
3443 moduleMetadata = other.moduleMetadata;
3444 asmJS = other.asmJS;
3446 #ifdef DEBUG
3447 assertNoExternalDependency();
3448 #endif
3450 return true;
3453 bool ExtensibleCompilationStencil::cloneFrom(FrontendContext* fc,
3454 const CompilationStencil& other) {
3455 return cloneFromImpl(fc, other);
3457 bool ExtensibleCompilationStencil::cloneFrom(
3458 FrontendContext* fc, const ExtensibleCompilationStencil& other) {
3459 return cloneFromImpl(fc, other);
3462 bool ExtensibleCompilationStencil::steal(FrontendContext* fc,
3463 RefPtr<CompilationStencil>&& other) {
3464 MOZ_ASSERT(alloc.isEmpty());
3465 using StorageType = CompilationStencil::StorageType;
3466 StorageType storageType = other->storageType;
3467 if (other->refCount > 1) {
3468 storageType = StorageType::Borrowed;
3471 if (storageType == StorageType::OwnedExtensible) {
3472 auto& otherExtensible = other->ownedBorrowStencil;
3474 canLazilyParse = otherExtensible->canLazilyParse;
3475 functionKey = otherExtensible->functionKey;
3477 alloc.steal(&otherExtensible->alloc);
3479 source = std::move(otherExtensible->source);
3481 scriptData = std::move(otherExtensible->scriptData);
3482 scriptExtra = std::move(otherExtensible->scriptExtra);
3483 gcThingData = std::move(otherExtensible->gcThingData);
3484 scopeData = std::move(otherExtensible->scopeData);
3485 scopeNames = std::move(otherExtensible->scopeNames);
3486 regExpData = std::move(otherExtensible->regExpData);
3487 bigIntData = std::move(otherExtensible->bigIntData);
3488 objLiteralData = std::move(otherExtensible->objLiteralData);
3490 parserAtoms = std::move(otherExtensible->parserAtoms);
3491 parserAtoms.fixupAlloc(alloc);
3493 sharedData = std::move(otherExtensible->sharedData);
3494 moduleMetadata = std::move(otherExtensible->moduleMetadata);
3495 asmJS = std::move(otherExtensible->asmJS);
3497 #ifdef DEBUG
3498 assertNoExternalDependency();
3499 #endif
3501 return true;
3504 if (storageType == StorageType::Borrowed) {
3505 return cloneFrom(fc, *other);
3508 MOZ_ASSERT(storageType == StorageType::Owned);
3510 canLazilyParse = other->canLazilyParse;
3511 functionKey = other->functionKey;
3513 #ifdef DEBUG
3514 other->assertNoExternalDependency();
3515 MOZ_ASSERT(other->refCount == 1);
3516 #endif
3518 // If CompilationStencil has no external dependency,
3519 // steal LifoAlloc and perform shallow copy.
3520 alloc.steal(&other->alloc);
3522 if (!CopySpanToVector(fc, scriptData, other->scriptData)) {
3523 return false;
3526 if (!CopySpanToVector(fc, scriptExtra, other->scriptExtra)) {
3527 return false;
3530 if (!CopySpanToVector(fc, gcThingData, other->gcThingData)) {
3531 return false;
3534 if (!CopySpanToVector(fc, scopeData, other->scopeData)) {
3535 return false;
3537 if (!CopySpanToVector(fc, scopeNames, other->scopeNames)) {
3538 return false;
3541 if (!CopySpanToVector(fc, regExpData, other->regExpData)) {
3542 return false;
3545 if (!CopySpanToVector(fc, bigIntData, other->bigIntData)) {
3546 return false;
3549 if (!CopySpanToVector(fc, objLiteralData, other->objLiteralData)) {
3550 return false;
3553 // Regardless of whether CompilationStencil has external dependency or not,
3554 // ParserAtoms should be interned, to populate internal HashMap.
3555 for (const auto* entry : other->parserAtomData) {
3556 if (!entry) {
3557 if (!parserAtoms.addPlaceholder(fc)) {
3558 return false;
3560 continue;
3563 auto index = parserAtoms.internExternalParserAtom(fc, entry);
3564 if (!index) {
3565 return false;
3569 sharedData = std::move(other->sharedData);
3570 moduleMetadata = std::move(other->moduleMetadata);
3571 asmJS = std::move(other->asmJS);
3573 #ifdef DEBUG
3574 assertNoExternalDependency();
3575 #endif
3577 return true;
3580 bool CompilationStencil::isModule() const {
3581 return scriptExtra[CompilationStencil::TopLevelIndex].isModule();
3584 bool ExtensibleCompilationStencil::isModule() const {
3585 return scriptExtra[CompilationStencil::TopLevelIndex].isModule();
3588 mozilla::Span<TaggedScriptThingIndex> ScriptStencil::gcthings(
3589 const CompilationStencil& stencil) const {
3590 return stencil.gcThingData.Subspan(gcThingsOffset, gcThingsLength);
3593 bool BigIntStencil::init(FrontendContext* fc, LifoAlloc& alloc,
3594 const mozilla::Span<const char16_t> buf) {
3595 #ifdef DEBUG
3596 // Assert we have no separators; if we have a separator then the algorithm
3597 // used in BigInt::literalIsZero will be incorrect.
3598 for (char16_t c : buf) {
3599 MOZ_ASSERT(c != '_');
3601 #endif
3602 size_t length = buf.size();
3603 char16_t* p = alloc.template newArrayUninitialized<char16_t>(length);
3604 if (!p) {
3605 ReportOutOfMemory(fc);
3606 return false;
3608 mozilla::PodCopy(p, buf.data(), length);
3609 source_ = mozilla::Span(p, length);
3610 return true;
3613 BigInt* BigIntStencil::createBigInt(JSContext* cx) const {
3614 mozilla::Range<const char16_t> source(source_.data(), source_.size());
3615 return js::ParseBigIntLiteral(cx, source);
3618 bool BigIntStencil::isZero() const {
3619 mozilla::Range<const char16_t> source(source_.data(), source_.size());
3620 return js::BigIntLiteralIsZero(source);
3623 #ifdef DEBUG
3624 bool BigIntStencil::isContainedIn(const LifoAlloc& alloc) const {
3625 return alloc.contains(source_.data());
3627 #endif
3629 #if defined(DEBUG) || defined(JS_JITSPEW)
3631 void frontend::DumpTaggedParserAtomIndex(js::JSONPrinter& json,
3632 TaggedParserAtomIndex taggedIndex,
3633 const CompilationStencil* stencil) {
3634 if (taggedIndex.isParserAtomIndex()) {
3635 json.property("tag", "AtomIndex");
3636 auto index = taggedIndex.toParserAtomIndex();
3637 if (stencil && stencil->parserAtomData[index]) {
3638 GenericPrinter& out = json.beginStringProperty("atom");
3639 stencil->parserAtomData[index]->dumpCharsNoQuote(out);
3640 json.endString();
3641 } else {
3642 json.property("index", size_t(index));
3644 return;
3647 if (taggedIndex.isWellKnownAtomId()) {
3648 json.property("tag", "WellKnown");
3649 auto index = taggedIndex.toWellKnownAtomId();
3650 switch (index) {
3651 case WellKnownAtomId::empty_:
3652 json.property("atom", "");
3653 break;
3655 # define CASE_(name, _) case WellKnownAtomId::name:
3656 FOR_EACH_NONTINY_COMMON_PROPERTYNAME(CASE_)
3657 # undef CASE_
3659 # define CASE_(name, _) case WellKnownAtomId::name:
3660 JS_FOR_EACH_PROTOTYPE(CASE_)
3661 # undef CASE_
3663 # define CASE_(name) case WellKnownAtomId::name:
3664 JS_FOR_EACH_WELL_KNOWN_SYMBOL(CASE_)
3665 # undef CASE_
3668 GenericPrinter& out = json.beginStringProperty("atom");
3669 ParserAtomsTable::dumpCharsNoQuote(out, index);
3670 json.endString();
3671 break;
3674 default:
3675 // This includes tiny WellKnownAtomId atoms, which is invalid.
3676 json.property("index", size_t(index));
3677 break;
3679 return;
3682 if (taggedIndex.isLength1StaticParserString()) {
3683 json.property("tag", "Length1Static");
3684 auto index = taggedIndex.toLength1StaticParserString();
3685 GenericPrinter& out = json.beginStringProperty("atom");
3686 ParserAtomsTable::dumpCharsNoQuote(out, index);
3687 json.endString();
3688 return;
3691 if (taggedIndex.isLength2StaticParserString()) {
3692 json.property("tag", "Length2Static");
3693 auto index = taggedIndex.toLength2StaticParserString();
3694 GenericPrinter& out = json.beginStringProperty("atom");
3695 ParserAtomsTable::dumpCharsNoQuote(out, index);
3696 json.endString();
3697 return;
3700 if (taggedIndex.isLength3StaticParserString()) {
3701 json.property("tag", "Length3Static");
3702 auto index = taggedIndex.toLength3StaticParserString();
3703 GenericPrinter& out = json.beginStringProperty("atom");
3704 ParserAtomsTable::dumpCharsNoQuote(out, index);
3705 json.endString();
3706 return;
3709 MOZ_ASSERT(taggedIndex.isNull());
3710 json.property("tag", "null");
3713 void frontend::DumpTaggedParserAtomIndexNoQuote(
3714 GenericPrinter& out, TaggedParserAtomIndex taggedIndex,
3715 const CompilationStencil* stencil) {
3716 if (taggedIndex.isParserAtomIndex()) {
3717 auto index = taggedIndex.toParserAtomIndex();
3718 if (stencil && stencil->parserAtomData[index]) {
3719 stencil->parserAtomData[index]->dumpCharsNoQuote(out);
3720 } else {
3721 out.printf("AtomIndex#%zu", size_t(index));
3723 return;
3726 if (taggedIndex.isWellKnownAtomId()) {
3727 auto index = taggedIndex.toWellKnownAtomId();
3728 switch (index) {
3729 case WellKnownAtomId::empty_:
3730 out.put("#<zero-length name>");
3731 break;
3733 # define CASE_(name, _) case WellKnownAtomId::name:
3734 FOR_EACH_NONTINY_COMMON_PROPERTYNAME(CASE_)
3735 # undef CASE_
3737 # define CASE_(name, _) case WellKnownAtomId::name:
3738 JS_FOR_EACH_PROTOTYPE(CASE_)
3739 # undef CASE_
3741 # define CASE_(name) case WellKnownAtomId::name:
3742 JS_FOR_EACH_WELL_KNOWN_SYMBOL(CASE_)
3743 # undef CASE_
3746 ParserAtomsTable::dumpCharsNoQuote(out, index);
3747 break;
3750 default:
3751 // This includes tiny WellKnownAtomId atoms, which is invalid.
3752 out.printf("WellKnown#%zu", size_t(index));
3753 break;
3755 return;
3758 if (taggedIndex.isLength1StaticParserString()) {
3759 auto index = taggedIndex.toLength1StaticParserString();
3760 ParserAtomsTable::dumpCharsNoQuote(out, index);
3761 return;
3764 if (taggedIndex.isLength2StaticParserString()) {
3765 auto index = taggedIndex.toLength2StaticParserString();
3766 ParserAtomsTable::dumpCharsNoQuote(out, index);
3767 return;
3770 if (taggedIndex.isLength3StaticParserString()) {
3771 auto index = taggedIndex.toLength3StaticParserString();
3772 ParserAtomsTable::dumpCharsNoQuote(out, index);
3773 return;
3776 MOZ_ASSERT(taggedIndex.isNull());
3777 out.put("#<null name>");
3780 void RegExpStencil::dump() const {
3781 js::Fprinter out(stderr);
3782 js::JSONPrinter json(out);
3783 dump(json, nullptr);
3786 void RegExpStencil::dump(js::JSONPrinter& json,
3787 const CompilationStencil* stencil) const {
3788 json.beginObject();
3789 dumpFields(json, stencil);
3790 json.endObject();
3793 void RegExpStencil::dumpFields(js::JSONPrinter& json,
3794 const CompilationStencil* stencil) const {
3795 json.beginObjectProperty("pattern");
3796 DumpTaggedParserAtomIndex(json, atom_, stencil);
3797 json.endObject();
3799 GenericPrinter& out = json.beginStringProperty("flags");
3801 if (flags().global()) {
3802 out.put("g");
3804 if (flags().ignoreCase()) {
3805 out.put("i");
3807 if (flags().multiline()) {
3808 out.put("m");
3810 if (flags().dotAll()) {
3811 out.put("s");
3813 if (flags().unicode()) {
3814 out.put("u");
3816 if (flags().sticky()) {
3817 out.put("y");
3820 json.endStringProperty();
3823 void BigIntStencil::dump() const {
3824 js::Fprinter out(stderr);
3825 js::JSONPrinter json(out);
3826 dump(json);
3829 void BigIntStencil::dump(js::JSONPrinter& json) const {
3830 GenericPrinter& out = json.beginString();
3831 dumpCharsNoQuote(out);
3832 json.endString();
3835 void BigIntStencil::dumpCharsNoQuote(GenericPrinter& out) const {
3836 for (char16_t c : source_) {
3837 out.putChar(char(c));
3841 void ScopeStencil::dump() const {
3842 js::Fprinter out(stderr);
3843 js::JSONPrinter json(out);
3844 dump(json, nullptr, nullptr);
3847 void ScopeStencil::dump(js::JSONPrinter& json,
3848 const BaseParserScopeData* baseScopeData,
3849 const CompilationStencil* stencil) const {
3850 json.beginObject();
3851 dumpFields(json, baseScopeData, stencil);
3852 json.endObject();
3855 void ScopeStencil::dumpFields(js::JSONPrinter& json,
3856 const BaseParserScopeData* baseScopeData,
3857 const CompilationStencil* stencil) const {
3858 json.property("kind", ScopeKindString(kind_));
3860 if (hasEnclosing()) {
3861 json.formatProperty("enclosing", "ScopeIndex(%zu)", size_t(enclosing()));
3864 json.property("firstFrameSlot", firstFrameSlot_);
3866 if (hasEnvironmentShape()) {
3867 json.formatProperty("numEnvironmentSlots", "%zu",
3868 size_t(numEnvironmentSlots_));
3871 if (isFunction()) {
3872 json.formatProperty("functionIndex", "ScriptIndex(%zu)",
3873 size_t(functionIndex_));
3876 json.beginListProperty("flags");
3877 if (flags_ & HasEnclosing) {
3878 json.value("HasEnclosing");
3880 if (flags_ & HasEnvironmentShape) {
3881 json.value("HasEnvironmentShape");
3883 if (flags_ & IsArrow) {
3884 json.value("IsArrow");
3886 json.endList();
3888 if (!baseScopeData) {
3889 return;
3892 json.beginObjectProperty("data");
3894 mozilla::Span<const ParserBindingName> trailingNames;
3895 switch (kind_) {
3896 case ScopeKind::Function: {
3897 const auto* data =
3898 static_cast<const FunctionScope::ParserData*>(baseScopeData);
3899 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3900 json.property("hasParameterExprs", data->slotInfo.hasParameterExprs());
3901 json.property("nonPositionalFormalStart",
3902 data->slotInfo.nonPositionalFormalStart);
3903 json.property("varStart", data->slotInfo.varStart);
3905 trailingNames = GetScopeDataTrailingNames(data);
3906 break;
3909 case ScopeKind::FunctionBodyVar: {
3910 const auto* data =
3911 static_cast<const VarScope::ParserData*>(baseScopeData);
3912 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3914 trailingNames = GetScopeDataTrailingNames(data);
3915 break;
3918 case ScopeKind::Lexical:
3919 case ScopeKind::SimpleCatch:
3920 case ScopeKind::Catch:
3921 case ScopeKind::NamedLambda:
3922 case ScopeKind::StrictNamedLambda:
3923 case ScopeKind::FunctionLexical: {
3924 const auto* data =
3925 static_cast<const LexicalScope::ParserData*>(baseScopeData);
3926 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3927 json.property("constStart", data->slotInfo.constStart);
3929 trailingNames = GetScopeDataTrailingNames(data);
3930 break;
3933 case ScopeKind::ClassBody: {
3934 const auto* data =
3935 static_cast<const ClassBodyScope::ParserData*>(baseScopeData);
3936 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3937 json.property("privateMethodStart", data->slotInfo.privateMethodStart);
3939 trailingNames = GetScopeDataTrailingNames(data);
3940 break;
3943 case ScopeKind::With: {
3944 break;
3947 case ScopeKind::Eval:
3948 case ScopeKind::StrictEval: {
3949 const auto* data =
3950 static_cast<const EvalScope::ParserData*>(baseScopeData);
3951 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3953 trailingNames = GetScopeDataTrailingNames(data);
3954 break;
3957 case ScopeKind::Global:
3958 case ScopeKind::NonSyntactic: {
3959 const auto* data =
3960 static_cast<const GlobalScope::ParserData*>(baseScopeData);
3961 json.property("letStart", data->slotInfo.letStart);
3962 json.property("constStart", data->slotInfo.constStart);
3964 trailingNames = GetScopeDataTrailingNames(data);
3965 break;
3968 case ScopeKind::Module: {
3969 const auto* data =
3970 static_cast<const ModuleScope::ParserData*>(baseScopeData);
3971 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3972 json.property("varStart", data->slotInfo.varStart);
3973 json.property("letStart", data->slotInfo.letStart);
3974 json.property("constStart", data->slotInfo.constStart);
3976 trailingNames = GetScopeDataTrailingNames(data);
3977 break;
3980 case ScopeKind::WasmInstance: {
3981 const auto* data =
3982 static_cast<const WasmInstanceScope::ParserData*>(baseScopeData);
3983 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3984 json.property("globalsStart", data->slotInfo.globalsStart);
3986 trailingNames = GetScopeDataTrailingNames(data);
3987 break;
3990 case ScopeKind::WasmFunction: {
3991 const auto* data =
3992 static_cast<const WasmFunctionScope::ParserData*>(baseScopeData);
3993 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
3995 trailingNames = GetScopeDataTrailingNames(data);
3996 break;
3999 default: {
4000 MOZ_CRASH("Unexpected ScopeKind");
4001 break;
4005 if (!trailingNames.empty()) {
4006 char index[64];
4007 json.beginObjectProperty("trailingNames");
4008 for (size_t i = 0; i < trailingNames.size(); i++) {
4009 const auto& name = trailingNames[i];
4010 SprintfLiteral(index, "%zu", i);
4011 json.beginObjectProperty(index);
4013 json.boolProperty("closedOver", name.closedOver());
4015 json.boolProperty("isTopLevelFunction", name.isTopLevelFunction());
4017 json.beginObjectProperty("name");
4018 DumpTaggedParserAtomIndex(json, name.name(), stencil);
4019 json.endObject();
4021 json.endObject();
4023 json.endObject();
4026 json.endObject();
4029 static void DumpModuleRequestVectorItems(
4030 js::JSONPrinter& json, const StencilModuleMetadata::RequestVector& requests,
4031 const CompilationStencil* stencil) {
4032 for (const auto& request : requests) {
4033 json.beginObject();
4034 if (request.specifier) {
4035 json.beginObjectProperty("specifier");
4036 DumpTaggedParserAtomIndex(json, request.specifier, stencil);
4037 json.endObject();
4039 json.endObject();
4043 static void DumpModuleEntryVectorItems(
4044 js::JSONPrinter& json, const StencilModuleMetadata::EntryVector& entries,
4045 const CompilationStencil* stencil) {
4046 for (const auto& entry : entries) {
4047 json.beginObject();
4048 if (entry.moduleRequest) {
4049 json.property("moduleRequest", entry.moduleRequest.value());
4051 if (entry.localName) {
4052 json.beginObjectProperty("localName");
4053 DumpTaggedParserAtomIndex(json, entry.localName, stencil);
4054 json.endObject();
4056 if (entry.importName) {
4057 json.beginObjectProperty("importName");
4058 DumpTaggedParserAtomIndex(json, entry.importName, stencil);
4059 json.endObject();
4061 if (entry.exportName) {
4062 json.beginObjectProperty("exportName");
4063 DumpTaggedParserAtomIndex(json, entry.exportName, stencil);
4064 json.endObject();
4066 // TODO: Dump assertions.
4067 json.endObject();
4071 void StencilModuleMetadata::dump() const {
4072 js::Fprinter out(stderr);
4073 js::JSONPrinter json(out);
4074 dump(json, nullptr);
4077 void StencilModuleMetadata::dump(js::JSONPrinter& json,
4078 const CompilationStencil* stencil) const {
4079 json.beginObject();
4080 dumpFields(json, stencil);
4081 json.endObject();
4084 void StencilModuleMetadata::dumpFields(
4085 js::JSONPrinter& json, const CompilationStencil* stencil) const {
4086 json.beginListProperty("moduleRequests");
4087 DumpModuleRequestVectorItems(json, moduleRequests, stencil);
4088 json.endList();
4090 json.beginListProperty("requestedModules");
4091 DumpModuleEntryVectorItems(json, requestedModules, stencil);
4092 json.endList();
4094 json.beginListProperty("importEntries");
4095 DumpModuleEntryVectorItems(json, importEntries, stencil);
4096 json.endList();
4098 json.beginListProperty("localExportEntries");
4099 DumpModuleEntryVectorItems(json, localExportEntries, stencil);
4100 json.endList();
4102 json.beginListProperty("indirectExportEntries");
4103 DumpModuleEntryVectorItems(json, indirectExportEntries, stencil);
4104 json.endList();
4106 json.beginListProperty("starExportEntries");
4107 DumpModuleEntryVectorItems(json, starExportEntries, stencil);
4108 json.endList();
4110 json.beginListProperty("functionDecls");
4111 for (const auto& index : functionDecls) {
4112 json.value("ScriptIndex(%zu)", size_t(index));
4114 json.endList();
4116 json.boolProperty("isAsync", isAsync);
4119 void js::DumpImmutableScriptFlags(js::JSONPrinter& json,
4120 ImmutableScriptFlags immutableFlags) {
4121 for (uint32_t i = 1; i; i = i << 1) {
4122 if (uint32_t(immutableFlags) & i) {
4123 switch (ImmutableScriptFlagsEnum(i)) {
4124 case ImmutableScriptFlagsEnum::IsForEval:
4125 json.value("IsForEval");
4126 break;
4127 case ImmutableScriptFlagsEnum::IsModule:
4128 json.value("IsModule");
4129 break;
4130 case ImmutableScriptFlagsEnum::IsFunction:
4131 json.value("IsFunction");
4132 break;
4133 case ImmutableScriptFlagsEnum::SelfHosted:
4134 json.value("SelfHosted");
4135 break;
4136 case ImmutableScriptFlagsEnum::ForceStrict:
4137 json.value("ForceStrict");
4138 break;
4139 case ImmutableScriptFlagsEnum::HasNonSyntacticScope:
4140 json.value("HasNonSyntacticScope");
4141 break;
4142 case ImmutableScriptFlagsEnum::NoScriptRval:
4143 json.value("NoScriptRval");
4144 break;
4145 case ImmutableScriptFlagsEnum::TreatAsRunOnce:
4146 json.value("TreatAsRunOnce");
4147 break;
4148 case ImmutableScriptFlagsEnum::Strict:
4149 json.value("Strict");
4150 break;
4151 case ImmutableScriptFlagsEnum::HasModuleGoal:
4152 json.value("HasModuleGoal");
4153 break;
4154 case ImmutableScriptFlagsEnum::HasInnerFunctions:
4155 json.value("HasInnerFunctions");
4156 break;
4157 case ImmutableScriptFlagsEnum::HasDirectEval:
4158 json.value("HasDirectEval");
4159 break;
4160 case ImmutableScriptFlagsEnum::BindingsAccessedDynamically:
4161 json.value("BindingsAccessedDynamically");
4162 break;
4163 case ImmutableScriptFlagsEnum::HasCallSiteObj:
4164 json.value("HasCallSiteObj");
4165 break;
4166 case ImmutableScriptFlagsEnum::IsAsync:
4167 json.value("IsAsync");
4168 break;
4169 case ImmutableScriptFlagsEnum::IsGenerator:
4170 json.value("IsGenerator");
4171 break;
4172 case ImmutableScriptFlagsEnum::FunHasExtensibleScope:
4173 json.value("FunHasExtensibleScope");
4174 break;
4175 case ImmutableScriptFlagsEnum::FunctionHasThisBinding:
4176 json.value("FunctionHasThisBinding");
4177 break;
4178 case ImmutableScriptFlagsEnum::NeedsHomeObject:
4179 json.value("NeedsHomeObject");
4180 break;
4181 case ImmutableScriptFlagsEnum::IsDerivedClassConstructor:
4182 json.value("IsDerivedClassConstructor");
4183 break;
4184 case ImmutableScriptFlagsEnum::IsSyntheticFunction:
4185 json.value("IsSyntheticFunction");
4186 break;
4187 case ImmutableScriptFlagsEnum::UseMemberInitializers:
4188 json.value("UseMemberInitializers");
4189 break;
4190 case ImmutableScriptFlagsEnum::HasRest:
4191 json.value("HasRest");
4192 break;
4193 case ImmutableScriptFlagsEnum::NeedsFunctionEnvironmentObjects:
4194 json.value("NeedsFunctionEnvironmentObjects");
4195 break;
4196 case ImmutableScriptFlagsEnum::FunctionHasExtraBodyVarScope:
4197 json.value("FunctionHasExtraBodyVarScope");
4198 break;
4199 case ImmutableScriptFlagsEnum::ShouldDeclareArguments:
4200 json.value("ShouldDeclareArguments");
4201 break;
4202 case ImmutableScriptFlagsEnum::NeedsArgsObj:
4203 json.value("NeedsArgsObj");
4204 break;
4205 case ImmutableScriptFlagsEnum::HasMappedArgsObj:
4206 json.value("HasMappedArgsObj");
4207 break;
4208 case ImmutableScriptFlagsEnum::IsInlinableLargeFunction:
4209 json.value("IsInlinableLargeFunction");
4210 break;
4211 case ImmutableScriptFlagsEnum::FunctionHasNewTargetBinding:
4212 json.value("FunctionHasNewTargetBinding");
4213 break;
4214 case ImmutableScriptFlagsEnum::UsesArgumentsIntrinsics:
4215 json.value("UsesArgumentsIntrinsics");
4216 break;
4217 default:
4218 json.value("Unknown(%x)", i);
4219 break;
4225 void js::DumpFunctionFlagsItems(js::JSONPrinter& json,
4226 FunctionFlags functionFlags) {
4227 switch (functionFlags.kind()) {
4228 case FunctionFlags::FunctionKind::NormalFunction:
4229 json.value("NORMAL_KIND");
4230 break;
4231 case FunctionFlags::FunctionKind::AsmJS:
4232 json.value("ASMJS_KIND");
4233 break;
4234 case FunctionFlags::FunctionKind::Wasm:
4235 json.value("WASM_KIND");
4236 break;
4237 case FunctionFlags::FunctionKind::Arrow:
4238 json.value("ARROW_KIND");
4239 break;
4240 case FunctionFlags::FunctionKind::Method:
4241 json.value("METHOD_KIND");
4242 break;
4243 case FunctionFlags::FunctionKind::ClassConstructor:
4244 json.value("CLASSCONSTRUCTOR_KIND");
4245 break;
4246 case FunctionFlags::FunctionKind::Getter:
4247 json.value("GETTER_KIND");
4248 break;
4249 case FunctionFlags::FunctionKind::Setter:
4250 json.value("SETTER_KIND");
4251 break;
4252 default:
4253 json.value("Unknown(%x)", uint8_t(functionFlags.kind()));
4254 break;
4257 static_assert(FunctionFlags::FUNCTION_KIND_MASK == 0x0007,
4258 "FunctionKind should use the lowest 3 bits");
4259 for (uint16_t i = 1 << 3; i; i = i << 1) {
4260 if (functionFlags.toRaw() & i) {
4261 switch (FunctionFlags::Flags(i)) {
4262 case FunctionFlags::Flags::EXTENDED:
4263 json.value("EXTENDED");
4264 break;
4265 case FunctionFlags::Flags::SELF_HOSTED:
4266 json.value("SELF_HOSTED");
4267 break;
4268 case FunctionFlags::Flags::BASESCRIPT:
4269 json.value("BASESCRIPT");
4270 break;
4271 case FunctionFlags::Flags::SELFHOSTLAZY:
4272 json.value("SELFHOSTLAZY");
4273 break;
4274 case FunctionFlags::Flags::CONSTRUCTOR:
4275 json.value("CONSTRUCTOR");
4276 break;
4277 case FunctionFlags::Flags::LAZY_ACCESSOR_NAME:
4278 json.value("LAZY_ACCESSOR_NAME");
4279 break;
4280 case FunctionFlags::Flags::LAMBDA:
4281 json.value("LAMBDA");
4282 break;
4283 case FunctionFlags::Flags::WASM_JIT_ENTRY:
4284 json.value("WASM_JIT_ENTRY");
4285 break;
4286 case FunctionFlags::Flags::HAS_INFERRED_NAME:
4287 json.value("HAS_INFERRED_NAME");
4288 break;
4289 case FunctionFlags::Flags::HAS_GUESSED_ATOM:
4290 json.value("HAS_GUESSED_ATOM");
4291 break;
4292 case FunctionFlags::Flags::RESOLVED_NAME:
4293 json.value("RESOLVED_NAME");
4294 break;
4295 case FunctionFlags::Flags::RESOLVED_LENGTH:
4296 json.value("RESOLVED_LENGTH");
4297 break;
4298 case FunctionFlags::Flags::GHOST_FUNCTION:
4299 json.value("GHOST_FUNCTION");
4300 break;
4301 default:
4302 json.value("Unknown(%x)", i);
4303 break;
4309 static void DumpScriptThing(js::JSONPrinter& json,
4310 const CompilationStencil* stencil,
4311 TaggedScriptThingIndex thing) {
4312 switch (thing.tag()) {
4313 case TaggedScriptThingIndex::Kind::ParserAtomIndex:
4314 case TaggedScriptThingIndex::Kind::WellKnown:
4315 json.beginObject();
4316 json.property("type", "Atom");
4317 DumpTaggedParserAtomIndex(json, thing.toAtom(), stencil);
4318 json.endObject();
4319 break;
4320 case TaggedScriptThingIndex::Kind::Null:
4321 json.nullValue();
4322 break;
4323 case TaggedScriptThingIndex::Kind::BigInt:
4324 json.value("BigIntIndex(%zu)", size_t(thing.toBigInt()));
4325 break;
4326 case TaggedScriptThingIndex::Kind::ObjLiteral:
4327 json.value("ObjLiteralIndex(%zu)", size_t(thing.toObjLiteral()));
4328 break;
4329 case TaggedScriptThingIndex::Kind::RegExp:
4330 json.value("RegExpIndex(%zu)", size_t(thing.toRegExp()));
4331 break;
4332 case TaggedScriptThingIndex::Kind::Scope:
4333 json.value("ScopeIndex(%zu)", size_t(thing.toScope()));
4334 break;
4335 case TaggedScriptThingIndex::Kind::Function:
4336 json.value("ScriptIndex(%zu)", size_t(thing.toFunction()));
4337 break;
4338 case TaggedScriptThingIndex::Kind::EmptyGlobalScope:
4339 json.value("EmptyGlobalScope");
4340 break;
4344 void ScriptStencil::dump() const {
4345 js::Fprinter out(stderr);
4346 js::JSONPrinter json(out);
4347 dump(json, nullptr);
4350 void ScriptStencil::dump(js::JSONPrinter& json,
4351 const CompilationStencil* stencil) const {
4352 json.beginObject();
4353 dumpFields(json, stencil);
4354 json.endObject();
4357 void ScriptStencil::dumpFields(js::JSONPrinter& json,
4358 const CompilationStencil* stencil) const {
4359 json.formatProperty("gcThingsOffset", "CompilationGCThingIndex(%u)",
4360 gcThingsOffset.index);
4361 json.property("gcThingsLength", gcThingsLength);
4363 if (stencil) {
4364 json.beginListProperty("gcThings");
4365 for (const auto& thing : gcthings(*stencil)) {
4366 DumpScriptThing(json, stencil, thing);
4368 json.endList();
4371 json.beginListProperty("flags");
4372 if (flags_ & WasEmittedByEnclosingScriptFlag) {
4373 json.value("WasEmittedByEnclosingScriptFlag");
4375 if (flags_ & AllowRelazifyFlag) {
4376 json.value("AllowRelazifyFlag");
4378 if (flags_ & HasSharedDataFlag) {
4379 json.value("HasSharedDataFlag");
4381 if (flags_ & HasLazyFunctionEnclosingScopeIndexFlag) {
4382 json.value("HasLazyFunctionEnclosingScopeIndexFlag");
4384 json.endList();
4386 if (isFunction()) {
4387 json.beginObjectProperty("functionAtom");
4388 DumpTaggedParserAtomIndex(json, functionAtom, stencil);
4389 json.endObject();
4391 json.beginListProperty("functionFlags");
4392 DumpFunctionFlagsItems(json, functionFlags);
4393 json.endList();
4395 if (hasLazyFunctionEnclosingScopeIndex()) {
4396 json.formatProperty("lazyFunctionEnclosingScopeIndex", "ScopeIndex(%zu)",
4397 size_t(lazyFunctionEnclosingScopeIndex()));
4400 if (hasSelfHostedCanonicalName()) {
4401 json.beginObjectProperty("selfHostCanonicalName");
4402 DumpTaggedParserAtomIndex(json, selfHostedCanonicalName(), stencil);
4403 json.endObject();
4408 void ScriptStencilExtra::dump() const {
4409 js::Fprinter out(stderr);
4410 js::JSONPrinter json(out);
4411 dump(json);
4414 void ScriptStencilExtra::dump(js::JSONPrinter& json) const {
4415 json.beginObject();
4416 dumpFields(json);
4417 json.endObject();
4420 void ScriptStencilExtra::dumpFields(js::JSONPrinter& json) const {
4421 json.beginListProperty("immutableFlags");
4422 DumpImmutableScriptFlags(json, immutableFlags);
4423 json.endList();
4425 json.beginObjectProperty("extent");
4426 json.property("sourceStart", extent.sourceStart);
4427 json.property("sourceEnd", extent.sourceEnd);
4428 json.property("toStringStart", extent.toStringStart);
4429 json.property("toStringEnd", extent.toStringEnd);
4430 json.property("lineno", extent.lineno);
4431 json.property("column", extent.column.zeroOriginValue());
4432 json.endObject();
4434 json.property("memberInitializers", memberInitializers_);
4436 json.property("nargs", nargs);
4439 void SharedDataContainer::dump() const {
4440 js::Fprinter out(stderr);
4441 js::JSONPrinter json(out);
4442 dump(json);
4445 void SharedDataContainer::dump(js::JSONPrinter& json) const {
4446 json.beginObject();
4447 dumpFields(json);
4448 json.endObject();
4451 void SharedDataContainer::dumpFields(js::JSONPrinter& json) const {
4452 if (isEmpty()) {
4453 json.nullProperty("ScriptIndex(0)");
4454 return;
4457 if (isSingle()) {
4458 json.formatProperty("ScriptIndex(0)", "u8[%zu]",
4459 asSingle()->immutableDataLength());
4460 return;
4463 if (isVector()) {
4464 auto& vec = *asVector();
4466 char index[64];
4467 for (size_t i = 0; i < vec.length(); i++) {
4468 SprintfLiteral(index, "ScriptIndex(%zu)", i);
4469 if (vec[i]) {
4470 json.formatProperty(index, "u8[%zu]", vec[i]->immutableDataLength());
4471 } else {
4472 json.nullProperty(index);
4475 return;
4478 if (isMap()) {
4479 auto& map = *asMap();
4481 char index[64];
4482 for (auto iter = map.iter(); !iter.done(); iter.next()) {
4483 SprintfLiteral(index, "ScriptIndex(%u)", iter.get().key().index);
4484 json.formatProperty(index, "u8[%zu]",
4485 iter.get().value()->immutableDataLength());
4487 return;
4490 MOZ_ASSERT(isBorrow());
4491 asBorrow()->dumpFields(json);
4494 struct DumpOptionsFields {
4495 js::JSONPrinter& json;
4497 void operator()(const char* name, JS::AsmJSOption value) {
4498 const char* valueStr = nullptr;
4499 switch (value) {
4500 case JS::AsmJSOption::Enabled:
4501 valueStr = "JS::AsmJSOption::Enabled";
4502 break;
4503 case JS::AsmJSOption::DisabledByAsmJSPref:
4504 valueStr = "JS::AsmJSOption::DisabledByAsmJSPref";
4505 break;
4506 case JS::AsmJSOption::DisabledByLinker:
4507 valueStr = "JS::AsmJSOption::DisabledByLinker";
4508 break;
4509 case JS::AsmJSOption::DisabledByNoWasmCompiler:
4510 valueStr = "JS::AsmJSOption::DisabledByNoWasmCompiler";
4511 break;
4512 case JS::AsmJSOption::DisabledByDebugger:
4513 valueStr = "JS::AsmJSOption::DisabledByDebugger";
4514 break;
4516 json.property(name, valueStr);
4519 void operator()(const char* name, JS::DelazificationOption value) {
4520 const char* valueStr = nullptr;
4521 switch (value) {
4522 # define SelectValueStr_(Strategy) \
4523 case JS::DelazificationOption::Strategy: \
4524 valueStr = "JS::DelazificationOption::" #Strategy; \
4525 break;
4527 FOREACH_DELAZIFICATION_STRATEGY(SelectValueStr_)
4528 # undef SelectValueStr_
4530 json.property(name, valueStr);
4533 void operator()(const char* name, char16_t* value) {}
4535 void operator()(const char* name, bool value) { json.property(name, value); }
4537 void operator()(const char* name, uint32_t value) {
4538 json.property(name, value);
4541 void operator()(const char* name, uint64_t value) {
4542 json.property(name, value);
4545 void operator()(const char* name, const char* value) {
4546 if (value) {
4547 json.property(name, value);
4548 return;
4550 json.nullProperty(name);
4553 void operator()(const char* name, JS::ConstUTF8CharsZ value) {
4554 if (value) {
4555 json.property(name, value.c_str());
4556 return;
4558 json.nullProperty(name);
4562 static void DumpOptionsFields(js::JSONPrinter& json,
4563 const JS::ReadOnlyCompileOptions& options) {
4564 struct DumpOptionsFields printer {
4565 json
4567 options.dumpWith(printer);
4570 static void DumpInputScopeFields(js::JSONPrinter& json,
4571 const InputScope& scope) {
4572 json.property("kind", ScopeKindString(scope.kind()));
4574 InputScope enclosing = scope.enclosing();
4575 if (enclosing.isNull()) {
4576 json.nullProperty("enclosing");
4577 } else {
4578 json.beginObjectProperty("enclosing");
4579 DumpInputScopeFields(json, enclosing);
4580 json.endObject();
4584 static void DumpInputScriptFields(js::JSONPrinter& json,
4585 const InputScript& script) {
4586 json.beginObjectProperty("extent");
4588 SourceExtent extent = script.extent();
4589 json.property("sourceStart", extent.sourceStart);
4590 json.property("sourceEnd", extent.sourceEnd);
4591 json.property("toStringStart", extent.toStringStart);
4592 json.property("toStringEnd", extent.toStringEnd);
4593 json.property("lineno", extent.lineno);
4594 json.property("column", extent.column.zeroOriginValue());
4596 json.endObject();
4598 json.beginListProperty("immutableFlags");
4599 DumpImmutableScriptFlags(json, script.immutableFlags());
4600 json.endList();
4602 json.beginListProperty("functionFlags");
4603 DumpFunctionFlagsItems(json, script.functionFlags());
4604 json.endList();
4606 json.property("hasPrivateScriptData", script.hasPrivateScriptData());
4608 InputScope scope = script.enclosingScope();
4609 if (scope.isNull()) {
4610 json.nullProperty("enclosingScope");
4611 } else {
4612 json.beginObjectProperty("enclosingScope");
4613 DumpInputScopeFields(json, scope);
4614 json.endObject();
4617 if (script.useMemberInitializers()) {
4618 json.property("memberInitializers",
4619 script.getMemberInitializers().serialize());
4623 void CompilationInput::dump() const {
4624 js::Fprinter out(stderr);
4625 js::JSONPrinter json(out);
4626 dump(json);
4627 out.put("\n");
4630 void CompilationInput::dump(js::JSONPrinter& json) const {
4631 json.beginObject();
4632 dumpFields(json);
4633 json.endObject();
4636 void CompilationInput::dumpFields(js::JSONPrinter& json) const {
4637 const char* targetStr = nullptr;
4638 switch (target) {
4639 case CompilationTarget::Global:
4640 targetStr = "CompilationTarget::Global";
4641 break;
4642 case CompilationTarget::SelfHosting:
4643 targetStr = "CompilationTarget::SelfHosting";
4644 break;
4645 case CompilationTarget::StandaloneFunction:
4646 targetStr = "CompilationTarget::StandaloneFunction";
4647 break;
4648 case CompilationTarget::StandaloneFunctionInNonSyntacticScope:
4649 targetStr = "CompilationTarget::StandaloneFunctionInNonSyntacticScope";
4650 break;
4651 case CompilationTarget::Eval:
4652 targetStr = "CompilationTarget::Eval";
4653 break;
4654 case CompilationTarget::Module:
4655 targetStr = "CompilationTarget::Module";
4656 break;
4657 case CompilationTarget::Delazification:
4658 targetStr = "CompilationTarget::Delazification";
4659 break;
4661 json.property("target", targetStr);
4663 json.beginObjectProperty("options");
4664 DumpOptionsFields(json, options);
4665 json.endObject();
4667 if (lazy_.isNull()) {
4668 json.nullProperty("lazy_");
4669 } else {
4670 json.beginObjectProperty("lazy_");
4671 DumpInputScriptFields(json, lazy_);
4672 json.endObject();
4675 if (enclosingScope.isNull()) {
4676 json.nullProperty("enclosingScope");
4677 } else {
4678 json.beginObjectProperty("enclosingScope");
4679 DumpInputScopeFields(json, enclosingScope);
4680 json.endObject();
4683 // TODO: Support printing the atomCache and the source fields.
4686 void CompilationStencil::dump() const {
4687 js::Fprinter out(stderr);
4688 js::JSONPrinter json(out);
4689 dump(json);
4690 out.put("\n");
4693 void CompilationStencil::dump(js::JSONPrinter& json) const {
4694 json.beginObject();
4695 dumpFields(json);
4696 json.endObject();
4699 void CompilationStencil::dumpFields(js::JSONPrinter& json) const {
4700 char index[64];
4702 json.beginObjectProperty("scriptData");
4703 for (size_t i = 0; i < scriptData.size(); i++) {
4704 SprintfLiteral(index, "ScriptIndex(%zu)", i);
4705 json.beginObjectProperty(index);
4706 scriptData[i].dumpFields(json, this);
4707 json.endObject();
4709 json.endObject();
4711 json.beginObjectProperty("scriptExtra");
4712 for (size_t i = 0; i < scriptExtra.size(); i++) {
4713 SprintfLiteral(index, "ScriptIndex(%zu)", i);
4714 json.beginObjectProperty(index);
4715 scriptExtra[i].dumpFields(json);
4716 json.endObject();
4718 json.endObject();
4720 json.beginObjectProperty("scopeData");
4721 MOZ_ASSERT(scopeData.size() == scopeNames.size());
4722 for (size_t i = 0; i < scopeData.size(); i++) {
4723 SprintfLiteral(index, "ScopeIndex(%zu)", i);
4724 json.beginObjectProperty(index);
4725 scopeData[i].dumpFields(json, scopeNames[i], this);
4726 json.endObject();
4728 json.endObject();
4730 json.beginObjectProperty("sharedData");
4731 sharedData.dumpFields(json);
4732 json.endObject();
4734 json.beginObjectProperty("regExpData");
4735 for (size_t i = 0; i < regExpData.size(); i++) {
4736 SprintfLiteral(index, "RegExpIndex(%zu)", i);
4737 json.beginObjectProperty(index);
4738 regExpData[i].dumpFields(json, this);
4739 json.endObject();
4741 json.endObject();
4743 json.beginObjectProperty("bigIntData");
4744 for (size_t i = 0; i < bigIntData.size(); i++) {
4745 SprintfLiteral(index, "BigIntIndex(%zu)", i);
4746 GenericPrinter& out = json.beginStringProperty(index);
4747 bigIntData[i].dumpCharsNoQuote(out);
4748 json.endStringProperty();
4750 json.endObject();
4752 json.beginObjectProperty("objLiteralData");
4753 for (size_t i = 0; i < objLiteralData.size(); i++) {
4754 SprintfLiteral(index, "ObjLiteralIndex(%zu)", i);
4755 json.beginObjectProperty(index);
4756 objLiteralData[i].dumpFields(json, this);
4757 json.endObject();
4759 json.endObject();
4761 if (moduleMetadata) {
4762 json.beginObjectProperty("moduleMetadata");
4763 moduleMetadata->dumpFields(json, this);
4764 json.endObject();
4767 json.beginObjectProperty("asmJS");
4768 if (asmJS) {
4769 for (auto iter = asmJS->moduleMap.iter(); !iter.done(); iter.next()) {
4770 SprintfLiteral(index, "ScriptIndex(%u)", iter.get().key().index);
4771 json.formatProperty(index, "asm.js");
4774 json.endObject();
4777 void CompilationStencil::dumpAtom(TaggedParserAtomIndex index) const {
4778 js::Fprinter out(stderr);
4779 js::JSONPrinter json(out);
4780 json.beginObject();
4781 DumpTaggedParserAtomIndex(json, index, this);
4782 json.endObject();
4785 void ExtensibleCompilationStencil::dump() {
4786 frontend::BorrowingCompilationStencil borrowingStencil(*this);
4787 borrowingStencil.dump();
4790 void ExtensibleCompilationStencil::dump(js::JSONPrinter& json) {
4791 frontend::BorrowingCompilationStencil borrowingStencil(*this);
4792 borrowingStencil.dump(json);
4795 void ExtensibleCompilationStencil::dumpFields(js::JSONPrinter& json) {
4796 frontend::BorrowingCompilationStencil borrowingStencil(*this);
4797 borrowingStencil.dumpFields(json);
4800 void ExtensibleCompilationStencil::dumpAtom(TaggedParserAtomIndex index) {
4801 frontend::BorrowingCompilationStencil borrowingStencil(*this);
4802 borrowingStencil.dumpAtom(index);
4805 #endif // defined(DEBUG) || defined(JS_JITSPEW)
4807 JSString* CompilationAtomCache::getExistingStringAt(
4808 ParserAtomIndex index) const {
4809 MOZ_RELEASE_ASSERT(atoms_.length() >= index);
4810 return atoms_[index];
4813 JSString* CompilationAtomCache::getExistingStringAt(
4814 JSContext* cx, TaggedParserAtomIndex taggedIndex) const {
4815 if (taggedIndex.isParserAtomIndex()) {
4816 auto index = taggedIndex.toParserAtomIndex();
4817 return getExistingStringAt(index);
4820 if (taggedIndex.isWellKnownAtomId()) {
4821 auto index = taggedIndex.toWellKnownAtomId();
4822 return GetWellKnownAtom(cx, index);
4825 if (taggedIndex.isLength1StaticParserString()) {
4826 auto index = taggedIndex.toLength1StaticParserString();
4827 return cx->staticStrings().getUnit(char16_t(index));
4830 if (taggedIndex.isLength2StaticParserString()) {
4831 auto index = taggedIndex.toLength2StaticParserString();
4832 return cx->staticStrings().getLength2FromIndex(size_t(index));
4835 MOZ_ASSERT(taggedIndex.isLength3StaticParserString());
4836 auto index = taggedIndex.toLength3StaticParserString();
4837 return cx->staticStrings().getUint(uint32_t(index));
4840 JSString* CompilationAtomCache::getStringAt(ParserAtomIndex index) const {
4841 if (size_t(index) >= atoms_.length()) {
4842 return nullptr;
4844 return atoms_[index];
4847 JSAtom* CompilationAtomCache::getExistingAtomAt(ParserAtomIndex index) const {
4848 return &getExistingStringAt(index)->asAtom();
4851 JSAtom* CompilationAtomCache::getExistingAtomAt(
4852 JSContext* cx, TaggedParserAtomIndex taggedIndex) const {
4853 return &getExistingStringAt(cx, taggedIndex)->asAtom();
4856 JSAtom* CompilationAtomCache::getAtomAt(ParserAtomIndex index) const {
4857 if (size_t(index) >= atoms_.length()) {
4858 return nullptr;
4860 if (!atoms_[index]) {
4861 return nullptr;
4863 return &atoms_[index]->asAtom();
4866 bool CompilationAtomCache::hasAtomAt(ParserAtomIndex index) const {
4867 if (size_t(index) >= atoms_.length()) {
4868 return false;
4870 return !!atoms_[index];
4873 bool CompilationAtomCache::setAtomAt(FrontendContext* fc, ParserAtomIndex index,
4874 JSString* atom) {
4875 if (size_t(index) < atoms_.length()) {
4876 atoms_[index] = atom;
4877 return true;
4880 if (!atoms_.resize(size_t(index) + 1)) {
4881 ReportOutOfMemory(fc);
4882 return false;
4885 atoms_[index] = atom;
4886 return true;
4889 bool CompilationAtomCache::allocate(FrontendContext* fc, size_t length) {
4890 MOZ_ASSERT(length >= atoms_.length());
4891 if (length == atoms_.length()) {
4892 return true;
4895 if (!atoms_.resize(length)) {
4896 ReportOutOfMemory(fc);
4897 return false;
4900 return true;
4903 void CompilationAtomCache::stealBuffer(AtomCacheVector& atoms) {
4904 atoms_ = std::move(atoms);
4905 // Destroy elements, without unreserving.
4906 atoms_.clear();
4909 void CompilationAtomCache::releaseBuffer(AtomCacheVector& atoms) {
4910 atoms = std::move(atoms_);
4913 bool CompilationState::allocateGCThingsUninitialized(
4914 FrontendContext* fc, ScriptIndex scriptIndex, size_t length,
4915 TaggedScriptThingIndex** cursor) {
4916 MOZ_ASSERT(gcThingData.length() <= UINT32_MAX);
4918 auto gcThingsOffset = CompilationGCThingIndex(gcThingData.length());
4920 if (length > INDEX_LIMIT) {
4921 ReportAllocationOverflow(fc);
4922 return false;
4924 uint32_t gcThingsLength = length;
4926 if (!gcThingData.growByUninitialized(length)) {
4927 js::ReportOutOfMemory(fc);
4928 return false;
4931 if (gcThingData.length() > UINT32_MAX) {
4932 ReportAllocationOverflow(fc);
4933 return false;
4936 ScriptStencil& script = scriptData[scriptIndex];
4937 script.gcThingsOffset = gcThingsOffset;
4938 script.gcThingsLength = gcThingsLength;
4940 *cursor = gcThingData.begin() + gcThingsOffset;
4941 return true;
4944 bool CompilationState::appendScriptStencilAndData(FrontendContext* fc) {
4945 if (!scriptData.emplaceBack()) {
4946 js::ReportOutOfMemory(fc);
4947 return false;
4950 if (isInitialStencil()) {
4951 if (!scriptExtra.emplaceBack()) {
4952 scriptData.popBack();
4953 MOZ_ASSERT(scriptData.length() == scriptExtra.length());
4955 js::ReportOutOfMemory(fc);
4956 return false;
4960 return true;
4963 bool CompilationState::appendGCThings(
4964 FrontendContext* fc, ScriptIndex scriptIndex,
4965 mozilla::Span<const TaggedScriptThingIndex> things) {
4966 MOZ_ASSERT(gcThingData.length() <= UINT32_MAX);
4968 auto gcThingsOffset = CompilationGCThingIndex(gcThingData.length());
4970 if (things.size() > INDEX_LIMIT) {
4971 ReportAllocationOverflow(fc);
4972 return false;
4974 uint32_t gcThingsLength = uint32_t(things.size());
4976 if (!gcThingData.append(things.data(), things.size())) {
4977 js::ReportOutOfMemory(fc);
4978 return false;
4981 if (gcThingData.length() > UINT32_MAX) {
4982 ReportAllocationOverflow(fc);
4983 return false;
4986 ScriptStencil& script = scriptData[scriptIndex];
4987 script.gcThingsOffset = gcThingsOffset;
4988 script.gcThingsLength = gcThingsLength;
4989 return true;
4992 CompilationState::CompilationStatePosition CompilationState::getPosition() {
4993 return CompilationStatePosition{scriptData.length(),
4994 asmJS ? asmJS->moduleMap.count() : 0};
4997 void CompilationState::rewind(
4998 const CompilationState::CompilationStatePosition& pos) {
4999 if (asmJS && asmJS->moduleMap.count() != pos.asmJSCount) {
5000 for (size_t i = pos.scriptDataLength; i < scriptData.length(); i++) {
5001 asmJS->moduleMap.remove(ScriptIndex(i));
5003 MOZ_ASSERT(asmJS->moduleMap.count() == pos.asmJSCount);
5005 // scriptExtra is empty for delazification.
5006 if (scriptExtra.length()) {
5007 MOZ_ASSERT(scriptExtra.length() == scriptData.length());
5008 scriptExtra.shrinkTo(pos.scriptDataLength);
5010 scriptData.shrinkTo(pos.scriptDataLength);
5013 void CompilationState::markGhost(
5014 const CompilationState::CompilationStatePosition& pos) {
5015 for (size_t i = pos.scriptDataLength; i < scriptData.length(); i++) {
5016 scriptData[i].setIsGhost();
5020 bool CompilationStencilMerger::buildFunctionKeyToIndex(FrontendContext* fc) {
5021 if (!functionKeyToInitialScriptIndex_.reserve(initial_->scriptExtra.length() -
5022 1)) {
5023 ReportOutOfMemory(fc);
5024 return false;
5027 for (size_t i = 1; i < initial_->scriptExtra.length(); i++) {
5028 const auto& extra = initial_->scriptExtra[i];
5029 auto key = extra.extent.toFunctionKey();
5031 // There can be multiple ScriptStencilExtra with same extent if
5032 // the function is parsed multiple times because of rewind for
5033 // arrow function, and in that case the last one's index should be used.
5034 // Overwrite with the last one.
5036 // Already reserved above, but OOMTest can hit failure mode in
5037 // HashTable::add.
5038 if (!functionKeyToInitialScriptIndex_.put(key, ScriptIndex(i))) {
5039 ReportOutOfMemory(fc);
5040 return false;
5044 return true;
5047 ScriptIndex CompilationStencilMerger::getInitialScriptIndexFor(
5048 const CompilationStencil& delazification) const {
5049 auto p = functionKeyToInitialScriptIndex_.lookup(delazification.functionKey);
5050 MOZ_ASSERT(p);
5051 return p->value();
5054 bool CompilationStencilMerger::buildAtomIndexMap(
5055 FrontendContext* fc, const CompilationStencil& delazification,
5056 AtomIndexMap& atomIndexMap) {
5057 uint32_t atomCount = delazification.parserAtomData.size();
5058 if (!atomIndexMap.reserve(atomCount)) {
5059 ReportOutOfMemory(fc);
5060 return false;
5062 for (const auto& atom : delazification.parserAtomData) {
5063 auto mappedIndex = initial_->parserAtoms.internExternalParserAtom(fc, atom);
5064 if (!mappedIndex) {
5065 return false;
5067 atomIndexMap.infallibleAppend(mappedIndex);
5069 return true;
5072 bool CompilationStencilMerger::setInitial(
5073 FrontendContext* fc, UniquePtr<ExtensibleCompilationStencil>&& initial) {
5074 MOZ_ASSERT(!initial_);
5076 initial_ = std::move(initial);
5078 return buildFunctionKeyToIndex(fc);
5081 template <typename GCThingIndexMapFunc, typename AtomIndexMapFunc,
5082 typename ScopeIndexMapFunc>
5083 static void MergeScriptStencil(ScriptStencil& dest, const ScriptStencil& src,
5084 GCThingIndexMapFunc mapGCThingIndex,
5085 AtomIndexMapFunc mapAtomIndex,
5086 ScopeIndexMapFunc mapScopeIndex,
5087 bool isTopLevel) {
5088 // If this function was lazy, all inner functions should have been lazy.
5089 MOZ_ASSERT(!dest.hasSharedData());
5091 // If the inner lazy function is skipped, gcThingsLength is empty.
5092 if (src.gcThingsLength) {
5093 dest.gcThingsOffset = mapGCThingIndex(src.gcThingsOffset);
5094 dest.gcThingsLength = src.gcThingsLength;
5097 if (src.functionAtom) {
5098 dest.functionAtom = mapAtomIndex(src.functionAtom);
5101 if (!dest.hasLazyFunctionEnclosingScopeIndex() &&
5102 src.hasLazyFunctionEnclosingScopeIndex()) {
5103 // Both enclosing function and this function were lazy, and
5104 // now enclosing function is non-lazy and this function is still lazy.
5105 dest.setLazyFunctionEnclosingScopeIndex(
5106 mapScopeIndex(src.lazyFunctionEnclosingScopeIndex()));
5107 } else if (dest.hasLazyFunctionEnclosingScopeIndex() &&
5108 !src.hasLazyFunctionEnclosingScopeIndex()) {
5109 // The enclosing function was non-lazy and this function was lazy, and
5110 // now this function is non-lazy.
5111 dest.resetHasLazyFunctionEnclosingScopeIndexAfterStencilMerge();
5112 } else {
5113 // The enclosing function is still lazy.
5114 MOZ_ASSERT(!dest.hasLazyFunctionEnclosingScopeIndex());
5115 MOZ_ASSERT(!src.hasLazyFunctionEnclosingScopeIndex());
5118 #ifdef DEBUG
5119 uint16_t BASESCRIPT = uint16_t(FunctionFlags::Flags::BASESCRIPT);
5120 uint16_t HAS_INFERRED_NAME =
5121 uint16_t(FunctionFlags::Flags::HAS_INFERRED_NAME);
5122 uint16_t HAS_GUESSED_ATOM = uint16_t(FunctionFlags::Flags::HAS_GUESSED_ATOM);
5123 uint16_t acceptableDifferenceForLazy = HAS_INFERRED_NAME | HAS_GUESSED_ATOM;
5124 uint16_t acceptableDifferenceForNonLazy =
5125 BASESCRIPT | HAS_INFERRED_NAME | HAS_GUESSED_ATOM;
5127 MOZ_ASSERT_IF(
5128 isTopLevel,
5129 (dest.functionFlags.toRaw() | acceptableDifferenceForNonLazy) ==
5130 (src.functionFlags.toRaw() | acceptableDifferenceForNonLazy));
5132 // NOTE: Currently we don't delazify inner functions.
5133 MOZ_ASSERT_IF(!isTopLevel,
5134 (dest.functionFlags.toRaw() | acceptableDifferenceForLazy) ==
5135 (src.functionFlags.toRaw() | acceptableDifferenceForLazy));
5136 #endif // DEBUG
5137 dest.functionFlags = src.functionFlags;
5139 // Other flags.
5141 if (src.wasEmittedByEnclosingScript()) {
5142 // NOTE: the top-level function of the delazification have
5143 // src.wasEmittedByEnclosingScript() == false, and that shouldn't
5144 // be copied.
5145 dest.setWasEmittedByEnclosingScript();
5148 if (src.allowRelazify()) {
5149 dest.setAllowRelazify();
5152 if (src.hasSharedData()) {
5153 dest.setHasSharedData();
5157 bool CompilationStencilMerger::addDelazification(
5158 FrontendContext* fc, const CompilationStencil& delazification) {
5159 MOZ_ASSERT(initial_);
5161 auto delazifiedFunctionIndex = getInitialScriptIndexFor(delazification);
5162 auto& destFun = initial_->scriptData[delazifiedFunctionIndex];
5164 if (destFun.hasSharedData()) {
5165 // If the function was already non-lazy, it means the following happened:
5166 // A. delazified twice within single incremental encoding
5167 // 1. this function is lazily parsed
5168 // 2. incremental encoding is started
5169 // 3. this function is delazified, encoded, and merged
5170 // 4. this function is relazified
5171 // 5. this function is delazified, encoded, and merged
5173 // B. delazified twice across decode
5174 // 1. this function is lazily parsed
5175 // 2. incremental encoding is started
5176 // 3. this function is delazified, encoded, and merged
5177 // 4. incremental encoding is finished
5178 // 5. decoded
5179 // 6. incremental encoding is started
5180 // here, this function is non-lazy
5181 // 7. this function is relazified
5182 // 8. this function is delazified, encoded, and merged
5184 // A can happen with public API.
5186 // B cannot happen with public API, but can happen if incremental
5187 // encoding at step B.6 is explicitly started by internal function.
5188 // See Evaluate and StartIncrementalEncoding in js/src/shell/js.cpp.
5189 return true;
5192 // If any failure happens, the initial stencil is left in the broken state.
5193 // Immediately discard it.
5194 auto failureCase = mozilla::MakeScopeExit([&] { initial_.reset(); });
5196 mozilla::Maybe<ScopeIndex> functionEnclosingScope;
5197 if (destFun.hasLazyFunctionEnclosingScopeIndex()) {
5198 // lazyFunctionEnclosingScopeIndex_ can be Nothing if this is
5199 // top-level function.
5200 functionEnclosingScope =
5201 mozilla::Some(destFun.lazyFunctionEnclosingScopeIndex());
5204 // A map from ParserAtomIndex in delazification to TaggedParserAtomIndex
5205 // in initial_.
5206 AtomIndexMap atomIndexMap;
5207 if (!buildAtomIndexMap(fc, delazification, atomIndexMap)) {
5208 return false;
5210 auto mapAtomIndex = [&](TaggedParserAtomIndex index) {
5211 if (index.isParserAtomIndex()) {
5212 return atomIndexMap[index.toParserAtomIndex()];
5215 return index;
5218 size_t gcThingOffset = initial_->gcThingData.length();
5219 size_t regExpOffset = initial_->regExpData.length();
5220 size_t bigIntOffset = initial_->bigIntData.length();
5221 size_t objLiteralOffset = initial_->objLiteralData.length();
5222 size_t scopeOffset = initial_->scopeData.length();
5224 // Map delazification's ScriptIndex to initial's ScriptIndex.
5226 // The lazy function's gcthings list stores inner function's ScriptIndex.
5227 // The n-th gcthing holds the ScriptIndex of the (n+1)-th script in
5228 // delazification.
5230 // NOTE: Currently we don't delazify inner functions.
5231 auto lazyFunctionGCThingsOffset = destFun.gcThingsOffset;
5232 auto mapScriptIndex = [&](ScriptIndex index) {
5233 if (index == CompilationStencil::TopLevelIndex) {
5234 return delazifiedFunctionIndex;
5237 return initial_->gcThingData[lazyFunctionGCThingsOffset + index.index - 1]
5238 .toFunction();
5241 // Map other delazification's indices into initial's indices.
5242 auto mapGCThingIndex = [&](CompilationGCThingIndex offset) {
5243 return CompilationGCThingIndex(gcThingOffset + offset.index);
5245 auto mapRegExpIndex = [&](RegExpIndex index) {
5246 return RegExpIndex(regExpOffset + index.index);
5248 auto mapBigIntIndex = [&](BigIntIndex index) {
5249 return BigIntIndex(bigIntOffset + index.index);
5251 auto mapObjLiteralIndex = [&](ObjLiteralIndex index) {
5252 return ObjLiteralIndex(objLiteralOffset + index.index);
5254 auto mapScopeIndex = [&](ScopeIndex index) {
5255 return ScopeIndex(scopeOffset + index.index);
5258 // Append gcThingData, with mapping TaggedScriptThingIndex.
5259 if (!initial_->gcThingData.append(delazification.gcThingData.data(),
5260 delazification.gcThingData.size())) {
5261 js::ReportOutOfMemory(fc);
5262 return false;
5264 for (size_t i = gcThingOffset; i < initial_->gcThingData.length(); i++) {
5265 auto& index = initial_->gcThingData[i];
5266 if (index.isNull()) {
5267 // Nothing to do.
5268 } else if (index.isAtom()) {
5269 index = TaggedScriptThingIndex(mapAtomIndex(index.toAtom()));
5270 } else if (index.isBigInt()) {
5271 index = TaggedScriptThingIndex(mapBigIntIndex(index.toBigInt()));
5272 } else if (index.isObjLiteral()) {
5273 index = TaggedScriptThingIndex(mapObjLiteralIndex(index.toObjLiteral()));
5274 } else if (index.isRegExp()) {
5275 index = TaggedScriptThingIndex(mapRegExpIndex(index.toRegExp()));
5276 } else if (index.isScope()) {
5277 index = TaggedScriptThingIndex(mapScopeIndex(index.toScope()));
5278 } else if (index.isFunction()) {
5279 index = TaggedScriptThingIndex(mapScriptIndex(index.toFunction()));
5280 } else {
5281 MOZ_ASSERT(index.isEmptyGlobalScope());
5282 // Nothing to do
5286 // Append regExpData, with mapping RegExpStencil.atom_.
5287 if (!initial_->regExpData.append(delazification.regExpData.data(),
5288 delazification.regExpData.size())) {
5289 js::ReportOutOfMemory(fc);
5290 return false;
5292 for (size_t i = regExpOffset; i < initial_->regExpData.length(); i++) {
5293 auto& data = initial_->regExpData[i];
5294 data.atom_ = mapAtomIndex(data.atom_);
5297 // Append bigIntData, with copying BigIntStencil.source_.
5298 if (!initial_->bigIntData.reserve(bigIntOffset +
5299 delazification.bigIntData.size())) {
5300 js::ReportOutOfMemory(fc);
5301 return false;
5303 for (const auto& data : delazification.bigIntData) {
5304 initial_->bigIntData.infallibleEmplaceBack();
5305 if (!initial_->bigIntData.back().init(fc, initial_->alloc, data.source())) {
5306 return false;
5310 // Append objLiteralData, with copying ObjLiteralStencil.code_, and mapping
5311 // TaggedParserAtomIndex in it.
5312 if (!initial_->objLiteralData.reserve(objLiteralOffset +
5313 delazification.objLiteralData.size())) {
5314 js::ReportOutOfMemory(fc);
5315 return false;
5317 for (const auto& data : delazification.objLiteralData) {
5318 size_t length = data.code().size();
5319 auto* code = initial_->alloc.newArrayUninitialized<uint8_t>(length);
5320 if (!code) {
5321 js::ReportOutOfMemory(fc);
5322 return false;
5324 memcpy(code, data.code().data(), length);
5326 ObjLiteralModifier modifier(mozilla::Span(code, length));
5327 modifier.mapAtom(mapAtomIndex);
5329 initial_->objLiteralData.infallibleEmplaceBack(
5330 code, length, data.kind(), data.flags(), data.propertyCount());
5333 // Append scopeData, with mapping indices in ScopeStencil fields.
5334 // And append scopeNames, with copying the entire data, and mapping
5335 // trailingNames.
5336 if (!initial_->scopeData.reserve(scopeOffset +
5337 delazification.scopeData.size())) {
5338 js::ReportOutOfMemory(fc);
5339 return false;
5341 if (!initial_->scopeNames.reserve(scopeOffset +
5342 delazification.scopeNames.size())) {
5343 js::ReportOutOfMemory(fc);
5344 return false;
5346 for (size_t i = 0; i < delazification.scopeData.size(); i++) {
5347 const auto& srcData = delazification.scopeData[i];
5348 const auto* srcNames = delazification.scopeNames[i];
5350 mozilla::Maybe<ScriptIndex> functionIndex = mozilla::Nothing();
5351 if (srcData.isFunction()) {
5352 // Inner functions should be in the same order as initial, beginning from
5353 // the delazification's index.
5354 functionIndex = mozilla::Some(mapScriptIndex(srcData.functionIndex()));
5357 BaseParserScopeData* destNames = nullptr;
5358 if (srcNames) {
5359 destNames = CopyScopeData(fc, initial_->alloc, srcData.kind(), srcNames);
5360 if (!destNames) {
5361 return false;
5363 auto trailingNames =
5364 GetParserScopeDataTrailingNames(srcData.kind(), destNames);
5365 for (auto& name : trailingNames) {
5366 if (name.name()) {
5367 name.updateNameAfterStencilMerge(mapAtomIndex(name.name()));
5372 initial_->scopeData.infallibleEmplaceBack(
5373 srcData.kind(),
5374 srcData.hasEnclosing()
5375 ? mozilla::Some(mapScopeIndex(srcData.enclosing()))
5376 : functionEnclosingScope,
5377 srcData.firstFrameSlot(),
5378 srcData.hasEnvironmentShape()
5379 ? mozilla::Some(srcData.numEnvironmentSlots())
5380 : mozilla::Nothing(),
5381 functionIndex, srcData.isArrow());
5383 initial_->scopeNames.infallibleEmplaceBack(destNames);
5386 // Add delazified function's shared data.
5388 // NOTE: Currently we don't delazify inner functions.
5389 if (!initial_->sharedData.addExtraWithoutShare(
5390 fc, delazifiedFunctionIndex,
5391 delazification.sharedData.get(CompilationStencil::TopLevelIndex))) {
5392 return false;
5395 // Update scriptData, with mapping indices in ScriptStencil fields.
5396 for (uint32_t i = 0; i < delazification.scriptData.size(); i++) {
5397 auto destIndex = mapScriptIndex(ScriptIndex(i));
5398 MergeScriptStencil(initial_->scriptData[destIndex],
5399 delazification.scriptData[i], mapGCThingIndex,
5400 mapAtomIndex, mapScopeIndex,
5401 i == CompilationStencil::TopLevelIndex);
5404 // WARNING: moduleMetadata and asmJS fields are known at script/module
5405 // top-level parsing, any mutation made in this function should be reflected
5406 // to ExtensibleCompilationStencil::steal and CompilationStencil::clone.
5408 // Function shouldn't be a module.
5409 MOZ_ASSERT(!delazification.moduleMetadata);
5411 // asm.js shouldn't appear inside delazification, given asm.js forces
5412 // full-parse.
5413 MOZ_ASSERT(!delazification.asmJS);
5415 failureCase.release();
5416 return true;
5419 void JS::StencilAddRef(JS::Stencil* stencil) { stencil->refCount++; }
5420 void JS::StencilRelease(JS::Stencil* stencil) {
5421 MOZ_RELEASE_ASSERT(stencil->refCount > 0);
5422 if (--stencil->refCount == 0) {
5423 js_delete(stencil);
5427 template <typename CharT>
5428 static already_AddRefed<JS::Stencil> CompileGlobalScriptToStencilImpl(
5429 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
5430 JS::SourceText<CharT>& srcBuf) {
5431 ScopeKind scopeKind =
5432 options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
5434 AutoReportFrontendContext fc(cx);
5435 NoScopeBindingCache scopeCache;
5436 Rooted<CompilationInput> input(cx, CompilationInput(options));
5437 RefPtr<JS::Stencil> stencil = js::frontend::CompileGlobalScriptToStencil(
5438 cx, &fc, cx->tempLifoAlloc(), input.get(), &scopeCache, srcBuf,
5439 scopeKind);
5440 if (!stencil) {
5441 return nullptr;
5444 // Convert the UniquePtr to a RefPtr and increment the count (to 1).
5445 return stencil.forget();
5448 already_AddRefed<JS::Stencil> JS::CompileGlobalScriptToStencil(
5449 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
5450 JS::SourceText<mozilla::Utf8Unit>& srcBuf) {
5451 return CompileGlobalScriptToStencilImpl(cx, options, srcBuf);
5454 already_AddRefed<JS::Stencil> JS::CompileGlobalScriptToStencil(
5455 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
5456 JS::SourceText<char16_t>& srcBuf) {
5457 return CompileGlobalScriptToStencilImpl(cx, options, srcBuf);
5460 template <typename CharT>
5461 static already_AddRefed<JS::Stencil> CompileModuleScriptToStencilImpl(
5462 JSContext* cx, const JS::ReadOnlyCompileOptions& optionsInput,
5463 JS::SourceText<CharT>& srcBuf) {
5464 JS::CompileOptions options(cx, optionsInput);
5465 options.setModule();
5467 AutoReportFrontendContext fc(cx);
5468 NoScopeBindingCache scopeCache;
5469 Rooted<CompilationInput> input(cx, CompilationInput(options));
5470 RefPtr<JS::Stencil> stencil = js::frontend::ParseModuleToStencil(
5471 cx, &fc, cx->tempLifoAlloc(), input.get(), &scopeCache, srcBuf);
5472 if (!stencil) {
5473 return nullptr;
5476 // Convert the UniquePtr to a RefPtr and increment the count (to 1).
5477 return stencil.forget();
5480 already_AddRefed<JS::Stencil> JS::CompileModuleScriptToStencil(
5481 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
5482 JS::SourceText<mozilla::Utf8Unit>& srcBuf) {
5483 return CompileModuleScriptToStencilImpl(cx, options, srcBuf);
5486 already_AddRefed<JS::Stencil> JS::CompileModuleScriptToStencil(
5487 JSContext* cx, const JS::ReadOnlyCompileOptions& options,
5488 JS::SourceText<char16_t>& srcBuf) {
5489 return CompileModuleScriptToStencilImpl(cx, options, srcBuf);
5492 JS_PUBLIC_API JSScript* JS::InstantiateGlobalStencil(
5493 JSContext* cx, const JS::InstantiateOptions& options, JS::Stencil* stencil,
5494 JS::InstantiationStorage* storage) {
5495 MOZ_ASSERT_IF(storage, storage->isValid());
5497 CompileOptions compileOptions(cx);
5498 options.copyTo(compileOptions);
5499 Rooted<CompilationInput> input(cx, CompilationInput(compileOptions));
5500 Rooted<CompilationGCOutput> gcOutput(cx);
5501 if (storage) {
5502 gcOutput.get().steal(std::move(*storage->gcOutput_));
5505 if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) {
5506 return nullptr;
5508 return gcOutput.get().script;
5511 JS_PUBLIC_API bool JS::StencilIsBorrowed(Stencil* stencil) {
5512 return stencil->storageType == CompilationStencil::StorageType::Borrowed;
5515 JS_PUBLIC_API JSObject* JS::InstantiateModuleStencil(
5516 JSContext* cx, const JS::InstantiateOptions& options, JS::Stencil* stencil,
5517 JS::InstantiationStorage* storage) {
5518 MOZ_ASSERT_IF(storage, storage->isValid());
5520 CompileOptions compileOptions(cx);
5521 options.copyTo(compileOptions);
5522 compileOptions.setModule();
5523 Rooted<CompilationInput> input(cx, CompilationInput(compileOptions));
5524 Rooted<CompilationGCOutput> gcOutput(cx);
5525 if (storage) {
5526 gcOutput.get().steal(std::move(*storage->gcOutput_));
5529 if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) {
5530 return nullptr;
5532 return gcOutput.get().module;
5535 JS::TranscodeResult JS::EncodeStencil(JSContext* cx, JS::Stencil* stencil,
5536 TranscodeBuffer& buffer) {
5537 AutoReportFrontendContext fc(cx);
5538 XDRStencilEncoder encoder(&fc, buffer);
5539 XDRResult res = encoder.codeStencil(*stencil);
5540 if (res.isErr()) {
5541 return res.unwrapErr();
5543 return TranscodeResult::Ok;
5546 JS::TranscodeResult JS::DecodeStencil(JSContext* cx,
5547 const JS::ReadOnlyDecodeOptions& options,
5548 const JS::TranscodeRange& range,
5549 JS::Stencil** stencilOut) {
5550 AutoReportFrontendContext fc(cx);
5551 return JS::DecodeStencil(&fc, options, range, stencilOut);
5554 JS::TranscodeResult JS::DecodeStencil(JS::FrontendContext* fc,
5555 const JS::ReadOnlyDecodeOptions& options,
5556 const JS::TranscodeRange& range,
5557 JS::Stencil** stencilOut) {
5558 RefPtr<ScriptSource> source = fc->getAllocator()->new_<ScriptSource>();
5559 if (!source) {
5560 return TranscodeResult::Throw;
5562 RefPtr<JS::Stencil> stencil(
5563 fc->getAllocator()->new_<CompilationStencil>(source));
5564 if (!stencil) {
5565 return TranscodeResult::Throw;
5567 XDRStencilDecoder decoder(fc, range);
5568 XDRResult res = decoder.codeStencil(options, *stencil);
5569 if (res.isErr()) {
5570 return res.unwrapErr();
5572 *stencilOut = stencil.forget().take();
5573 return TranscodeResult::Ok;
5576 JS_PUBLIC_API size_t JS::SizeOfStencil(Stencil* stencil,
5577 mozilla::MallocSizeOf mallocSizeOf) {
5578 return stencil->sizeOfIncludingThis(mallocSizeOf);
5581 JS::InstantiationStorage::~InstantiationStorage() {
5582 if (gcOutput_) {
5583 js_delete(gcOutput_);
5584 gcOutput_ = nullptr;