Stencil.cpp (207164B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "frontend/Stencil.h" 8 9 #include "mozilla/Assertions.h" // MOZ_RELEASE_ASSERT 10 #include "mozilla/CheckedInt.h" // mozilla::CheckedInt 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 17 18 #include <algorithm> // std::fill 19 #include <string.h> // strlen 20 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 "jit/BaselineCompileTask.h" // BaselineCompileTask::OffThreadBaselineCompilationAvailable 35 #include "jit/BaselineJIT.h" // jit::BaselineScript, jit::CanBaselineInterpretScript 36 #include "jit/JitContext.h" // jit::MethodStatus 37 #include "jit/JitRuntime.h" // jit::JitRuntime 38 #include "jit/JitScript.h" // AutoKeepJitScripts 39 #include "js/CallArgs.h" // JSNative 40 #include "js/CompileOptions.h" // JS::DecodeOptions, JS::ReadOnlyDecodeOptions 41 #include "js/DOMEventDispatch.h" // TRACE_FOR_TEST_DOM 42 #include "js/experimental/CompileScript.h" // JS::PrepareForInstantiate 43 #include "js/experimental/JSStencil.h" // JS::Stencil 44 #include "js/GCAPI.h" // JS::AutoCheckCannotGC 45 #include "js/Prefs.h" // JS::Prefs 46 #include "js/Printer.h" // js::Fprinter 47 #include "js/RealmOptions.h" // JS::RealmBehaviors 48 #include "js/RootingAPI.h" // Rooted 49 #include "js/Transcoding.h" // JS::TranscodeBuffer 50 #include "js/Utility.h" // js_malloc, js_calloc, js_free 51 #include "js/Value.h" // ObjectValue 52 #include "js/WasmModule.h" // JS::WasmModule 53 #include "vm/BigIntType.h" // ParseBigIntLiteral, BigInt::createFromInt64 54 #include "vm/BindingKind.h" // BindingKind 55 #include "vm/EnvironmentObject.h" 56 #include "vm/GeneratorAndAsyncKind.h" // GeneratorKind, FunctionAsyncKind 57 #include "vm/JSAtomUtils.h" // AtomToPrintableString 58 #include "vm/JSContext.h" // JSContext 59 #include "vm/JSFunction.h" // JSFunction, GetFunctionPrototype, NewFunctionWithProto 60 #include "vm/JSObject.h" // JSObject, TenuredObject 61 #include "vm/JSONPrinter.h" // js::JSONPrinter 62 #include "vm/JSScript.h" // BaseScript, JSScript 63 #include "vm/Realm.h" // JS::Realm 64 #include "vm/RegExpObject.h" // js::RegExpObject 65 #include "vm/Scope.h" // Scope, *Scope, ScopeKind::*, ScopeKindString, ScopeIter, ScopeKindIsCatch, BindingIter, GetScopeDataTrailingNames, SizeOfParserScopeData 66 #include "vm/ScopeKind.h" // ScopeKind 67 #include "vm/SelfHosting.h" // SetClonedSelfHostedFunctionName 68 #include "vm/StaticStrings.h" 69 #include "vm/StencilEnums.h" // ImmutableScriptFlagsEnum 70 #include "vm/StringType.h" // JSAtom, js::CopyChars 71 #include "wasm/AsmJS.h" // InstantiateAsmJS 72 73 #include "jit/JitHints-inl.h" // JitHints::mightHaveEagerBaselineHint 74 #include "jit/JitScript-inl.h" // AutoKeepJitScripts constructor 75 #include "vm/EnvironmentObject-inl.h" // JSObject::enclosingEnvironment 76 #include "vm/JSFunction-inl.h" // JSFunction::create 77 #include "vm/JSScript-inl.h" // JSScript::baselineScript 78 79 using namespace js; 80 using namespace js::frontend; 81 82 // These 2 functions are used to write the same code with lambda using auto 83 // arguments. The auto argument type is set by the Variant.match function of the 84 // InputScope variant. Thus dispatching to either a Scope* or to a 85 // ScopeStencilRef. This function can then be used as a way to specialize the 86 // code within the lambda without duplicating the code. 87 // 88 // Identically, an InputName is constructed using the scope type and the 89 // matching binding name type. This way, functions which are called by this 90 // lambda can manipulate an InputName and do not have to be duplicated. 91 // 92 // for (InputScopeIter si(...); si; si++) { 93 // si.scope().match([](auto& scope) { 94 // for (auto bi = InputBindingIter(scope); bi; bi++) { 95 // InputName name(scope, bi.name()); 96 // } 97 // }); 98 // } 99 static js::BindingIter InputBindingIter(Scope* ptr) { 100 return js::BindingIter(ptr); 101 } 102 103 static ParserBindingIter InputBindingIter(const ScopeStencilRef& ref) { 104 return ParserBindingIter(ref); 105 } 106 107 static ParserBindingIter InputBindingIter(const FakeStencilGlobalScope&) { 108 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No bindings on empty global."); 109 } 110 111 InputName InputScript::displayAtom() const { 112 return script_.match( 113 [](BaseScript* ptr) { 114 return InputName(ptr, ptr->function()->fullDisplayAtom()); 115 }, 116 [](const ScriptStencilRef& ref) { 117 auto& scriptData = ref.scriptDataFromEnclosing(); 118 return InputName(ref.enclosingScript(), scriptData.functionAtom); 119 }); 120 } 121 122 TaggedParserAtomIndex InputName::internInto(FrontendContext* fc, 123 ParserAtomsTable& parserAtoms, 124 CompilationAtomCache& atomCache) { 125 return variant_.match( 126 [&](JSAtom* ptr) -> TaggedParserAtomIndex { 127 return parserAtoms.internJSAtom(fc, atomCache, ptr); 128 }, 129 [&](NameStencilRef& ref) -> TaggedParserAtomIndex { 130 return parserAtoms.internExternalParserAtomIndex(fc, ref.context_, 131 ref.atomIndex_); 132 }); 133 } 134 135 bool InputName::isEqualTo(FrontendContext* fc, ParserAtomsTable& parserAtoms, 136 CompilationAtomCache& atomCache, 137 TaggedParserAtomIndex other, 138 JSAtom** otherCached) const { 139 return variant_.match( 140 [&](const JSAtom* ptr) -> bool { 141 if (ptr->hash() != parserAtoms.hash(other)) { 142 return false; 143 } 144 145 // JSAtom variant is used only on the main thread delazification, 146 // where JSContext is always available. 147 JSContext* cx = fc->maybeCurrentJSContext(); 148 MOZ_ASSERT(cx); 149 150 if (!*otherCached) { 151 // TODO-Stencil: 152 // Here, we convert our name into a JSAtom*, and hard-crash on failure 153 // to allocate. This conversion should not be required as we should be 154 // able to iterate up snapshotted scope chains that use parser atoms. 155 // 156 // This will be fixed when the enclosing scopes are snapshotted. 157 // 158 // See bug 1690277. 159 AutoEnterOOMUnsafeRegion oomUnsafe; 160 *otherCached = parserAtoms.toJSAtom(cx, fc, other, atomCache); 161 if (!*otherCached) { 162 oomUnsafe.crash("InputName::isEqualTo"); 163 } 164 } else { 165 MOZ_ASSERT(atomCache.getExistingAtomAt(cx, other) == *otherCached); 166 } 167 return ptr == *otherCached; 168 }, 169 [&](const NameStencilRef& ref) -> bool { 170 return parserAtoms.isEqualToExternalParserAtomIndex(other, ref.context_, 171 ref.atomIndex_); 172 }); 173 } 174 175 GenericAtom::GenericAtom(FrontendContext* fc, ParserAtomsTable& parserAtoms, 176 CompilationAtomCache& atomCache, 177 TaggedParserAtomIndex index) 178 : ref(EmitterName(fc, parserAtoms, atomCache, index)) { 179 hash = parserAtoms.hash(index); 180 } 181 182 GenericAtom::GenericAtom(const CompilationStencil& context, 183 TaggedParserAtomIndex index) 184 : ref(StencilName{context, index}) { 185 if (index.isParserAtomIndex()) { 186 ParserAtom* atom = context.parserAtomData[index.toParserAtomIndex()]; 187 hash = atom->hash(); 188 } else { 189 hash = index.staticOrWellKnownHash(); 190 } 191 } 192 193 GenericAtom::GenericAtom(ScopeStencilRef& scope, TaggedParserAtomIndex index) 194 : GenericAtom(*scope.context(), index) {} 195 196 BindingHasher<TaggedParserAtomIndex>::Lookup::Lookup(ScopeStencilRef& scope_ref, 197 const GenericAtom& other) 198 : keyStencil(*scope_ref.context()), other(other) {} 199 200 bool GenericAtom::operator==(const GenericAtom& other) const { 201 return ref.match( 202 [&other](const EmitterName& name) -> bool { 203 return other.ref.match( 204 [&name](const EmitterName& other) -> bool { 205 // We never have multiple Emitter context at the same time. 206 MOZ_ASSERT(name.fc == other.fc); 207 MOZ_ASSERT(&name.parserAtoms == &other.parserAtoms); 208 MOZ_ASSERT(&name.atomCache == &other.atomCache); 209 return name.index == other.index; 210 }, 211 [&name](const StencilName& other) -> bool { 212 return name.parserAtoms.isEqualToExternalParserAtomIndex( 213 name.index, other.stencil, other.index); 214 }, 215 [&name](JSAtom* other) -> bool { 216 // JSAtom variant is used only on the main thread delazification, 217 // where JSContext is always available. 218 JSContext* cx = name.fc->maybeCurrentJSContext(); 219 MOZ_ASSERT(cx); 220 AutoEnterOOMUnsafeRegion oomUnsafe; 221 JSAtom* namePtr = name.parserAtoms.toJSAtom( 222 cx, name.fc, name.index, name.atomCache); 223 if (!namePtr) { 224 oomUnsafe.crash("GenericAtom(EmitterName == JSAtom*)"); 225 } 226 return namePtr == other; 227 }); 228 }, 229 [&other](const StencilName& name) -> bool { 230 return other.ref.match( 231 [&name](const EmitterName& other) -> bool { 232 return other.parserAtoms.isEqualToExternalParserAtomIndex( 233 other.index, name.stencil, name.index); 234 }, 235 [&name](const StencilName& other) -> bool { 236 // Technically it is possible to have multiple stencils, but in 237 // this particular case let's assume we never encounter a case 238 // where we are comparing names from different stencils. 239 // 240 // The reason this assumption is safe today is that we are only 241 // using this in the context of a stencil-delazification, where 242 // the only StencilNames are coming from the CompilationStencil 243 // provided to CompilationInput::initFromStencil. 244 MOZ_ASSERT(&name.stencil == &other.stencil); 245 return name.index == other.index; 246 }, 247 [](JSAtom* other) -> bool { 248 MOZ_CRASH("Never used."); 249 return false; 250 }); 251 }, 252 [&other](JSAtom* name) -> bool { 253 return other.ref.match( 254 [&name](const EmitterName& other) -> bool { 255 // JSAtom variant is used only on the main thread delazification, 256 // where JSContext is always available. 257 JSContext* cx = other.fc->maybeCurrentJSContext(); 258 MOZ_ASSERT(cx); 259 AutoEnterOOMUnsafeRegion oomUnsafe; 260 JSAtom* otherPtr = other.parserAtoms.toJSAtom( 261 cx, other.fc, other.index, other.atomCache); 262 if (!otherPtr) { 263 oomUnsafe.crash("GenericAtom(JSAtom* == EmitterName)"); 264 } 265 return name == otherPtr; 266 }, 267 [](const StencilName& other) -> bool { 268 MOZ_CRASH("Never used."); 269 return false; 270 }, 271 [&name](JSAtom* other) -> bool { return name == other; }); 272 }); 273 } 274 275 #ifdef DEBUG 276 template <typename SpanT, typename VecT> 277 void AssertBorrowingSpan(const SpanT& span, const VecT& vec) { 278 MOZ_ASSERT(span.size() == vec.length()); 279 MOZ_ASSERT(span.data() == vec.begin()); 280 } 281 #endif 282 283 bool ScopeBindingCache::canCacheFor(Scope* ptr) { 284 MOZ_CRASH("Unexpected scope chain type: Scope*"); 285 } 286 287 bool ScopeBindingCache::canCacheFor(ScopeStencilRef ref) { 288 MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef"); 289 } 290 291 bool ScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& ref) { 292 MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope"); 293 } 294 295 BindingMap<JSAtom*>* ScopeBindingCache::createCacheFor(Scope* ptr) { 296 MOZ_CRASH("Unexpected scope chain type: Scope*"); 297 } 298 299 BindingMap<JSAtom*>* ScopeBindingCache::lookupScope(Scope* ptr, 300 CacheGeneration gen) { 301 MOZ_CRASH("Unexpected scope chain type: Scope*"); 302 } 303 304 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::createCacheFor( 305 ScopeStencilRef ref) { 306 MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef"); 307 } 308 309 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::lookupScope( 310 ScopeStencilRef ref, CacheGeneration gen) { 311 MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef"); 312 } 313 314 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::createCacheFor( 315 const FakeStencilGlobalScope& ref) { 316 MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope"); 317 } 318 319 BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::lookupScope( 320 const FakeStencilGlobalScope& ref, CacheGeneration gen) { 321 MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope"); 322 } 323 324 bool NoScopeBindingCache::canCacheFor(Scope* ptr) { return false; } 325 326 bool NoScopeBindingCache::canCacheFor(ScopeStencilRef ref) { return false; } 327 328 bool NoScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& ref) { 329 return false; 330 } 331 332 bool RuntimeScopeBindingCache::canCacheFor(Scope* ptr) { return true; } 333 334 BindingMap<JSAtom*>* RuntimeScopeBindingCache::createCacheFor(Scope* ptr) { 335 BaseScopeData* dataPtr = ptr->rawData(); 336 BindingMap<JSAtom*> bindingCache; 337 if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) { 338 return nullptr; 339 } 340 341 return lookupScope(ptr, cacheGeneration); 342 } 343 344 BindingMap<JSAtom*>* RuntimeScopeBindingCache::lookupScope( 345 Scope* ptr, CacheGeneration gen) { 346 MOZ_ASSERT(gen == cacheGeneration); 347 BaseScopeData* dataPtr = ptr->rawData(); 348 auto valuePtr = scopeMap.lookup(dataPtr); 349 if (!valuePtr) { 350 return nullptr; 351 } 352 return &valuePtr->value(); 353 } 354 355 bool StencilScopeBindingCache::canCacheFor(ScopeStencilRef ref) { return true; } 356 357 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::createCacheFor( 358 ScopeStencilRef ref) { 359 #ifdef DEBUG 360 MOZ_ASSERT(&ref.stencils_ == &stencils_); 361 MOZ_ASSERT_IF( 362 ref.stencils_.getInitial() != ref.context(), 363 ref.stencils_.getScriptIndexFor(ref.context()) == ref.scriptIndex_); 364 #endif 365 auto* dataPtr = ref.context()->scopeNames[ref.scopeIndex_]; 366 BindingMap<TaggedParserAtomIndex> bindingCache; 367 if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) { 368 return nullptr; 369 } 370 371 return lookupScope(ref, 1); 372 } 373 374 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::lookupScope( 375 ScopeStencilRef ref, CacheGeneration gen) { 376 #ifdef DEBUG 377 MOZ_ASSERT(&ref.stencils_ == &stencils_); 378 MOZ_ASSERT_IF( 379 ref.stencils_.getInitial() != ref.context(), 380 ref.stencils_.getScriptIndexFor(ref.context()) == ref.scriptIndex_); 381 #endif 382 auto* dataPtr = ref.context()->scopeNames[ref.scopeIndex_]; 383 auto ptr = scopeMap.lookup(dataPtr); 384 if (!ptr) { 385 return nullptr; 386 } 387 return &ptr->value(); 388 } 389 390 static AbstractBaseScopeData<TaggedParserAtomIndex> 391 moduleGlobalAbstractScopeData; 392 393 bool StencilScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& ref) { 394 return true; 395 } 396 397 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::createCacheFor( 398 const FakeStencilGlobalScope& ref) { 399 auto* dataPtr = &moduleGlobalAbstractScopeData; 400 BindingMap<TaggedParserAtomIndex> bindingCache; 401 if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) { 402 return nullptr; 403 } 404 405 return lookupScope(ref, 1); 406 } 407 408 BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::lookupScope( 409 const FakeStencilGlobalScope& ref, CacheGeneration gen) { 410 auto* dataPtr = &moduleGlobalAbstractScopeData; 411 auto ptr = scopeMap.lookup(dataPtr); 412 if (!ptr) { 413 return nullptr; 414 } 415 return &ptr->value(); 416 } 417 418 bool ScopeContext::init(FrontendContext* fc, CompilationInput& input, 419 ParserAtomsTable& parserAtoms, 420 ScopeBindingCache* scopeCache, InheritThis inheritThis, 421 JSObject* enclosingEnv) { 422 // Record the scopeCache to be used while looking up NameLocation bindings. 423 this->scopeCache = scopeCache; 424 scopeCacheGen = scopeCache->getCurrentGeneration(); 425 426 InputScope maybeNonDefaultEnclosingScope( 427 input.maybeNonDefaultEnclosingScope()); 428 429 // If this eval is in response to Debugger.Frame.eval, we may have an 430 // incomplete scope chain. In order to provide a better debugging experience, 431 // we inspect the (optional) environment chain to determine it's enclosing 432 // FunctionScope if there is one. If there is no such scope, we use the 433 // orignal scope provided. 434 // 435 // NOTE: This is used to compute the ThisBinding kind and to allow access to 436 // private fields and methods, while other contextual information only 437 // uses the actual scope passed to the compile. 438 auto effectiveScope = 439 determineEffectiveScope(maybeNonDefaultEnclosingScope, enclosingEnv); 440 441 if (inheritThis == InheritThis::Yes) { 442 computeThisBinding(effectiveScope); 443 computeThisEnvironment(maybeNonDefaultEnclosingScope); 444 } 445 computeInScope(maybeNonDefaultEnclosingScope); 446 447 cacheEnclosingScope(input.enclosingScope); 448 449 if (input.target == CompilationInput::CompilationTarget::Eval) { 450 if (!cacheEnclosingScopeBindingForEval(fc, input, parserAtoms)) { 451 return false; 452 } 453 if (!cachePrivateFieldsForEval(fc, input, enclosingEnv, effectiveScope, 454 parserAtoms)) { 455 return false; 456 } 457 } 458 459 return true; 460 } 461 462 void ScopeContext::computeThisEnvironment(const InputScope& enclosingScope) { 463 uint32_t envCount = 0; 464 for (InputScopeIter si(enclosingScope); si; si++) { 465 if (si.kind() == ScopeKind::Function) { 466 // Arrow function inherit the "this" environment of the enclosing script, 467 // so continue ignore them. 468 if (!si.scope().isArrow()) { 469 allowNewTarget = true; 470 471 if (si.scope().allowSuperProperty()) { 472 allowSuperProperty = true; 473 enclosingThisEnvironmentHops = envCount; 474 } 475 476 if (si.scope().isClassConstructor()) { 477 memberInitializers = 478 si.scope().useMemberInitializers() 479 ? mozilla::Some(si.scope().getMemberInitializers()) 480 : mozilla::Some(MemberInitializers::Empty()); 481 MOZ_ASSERT(memberInitializers->valid); 482 } else { 483 if (si.scope().isSyntheticFunction()) { 484 allowArguments = false; 485 } 486 } 487 488 if (si.scope().isDerivedClassConstructor()) { 489 allowSuperCall = true; 490 } 491 492 // Found the effective "this" environment, so stop. 493 return; 494 } 495 } 496 497 if (si.scope().hasEnvironment()) { 498 envCount++; 499 } 500 } 501 } 502 503 void ScopeContext::computeThisBinding(const InputScope& scope) { 504 // Inspect the scope-chain. 505 for (InputScopeIter si(scope); si; si++) { 506 if (si.kind() == ScopeKind::Module) { 507 thisBinding = ThisBinding::Module; 508 return; 509 } 510 511 if (si.kind() == ScopeKind::Function) { 512 // Arrow functions don't have their own `this` binding. 513 if (si.scope().isArrow()) { 514 continue; 515 } 516 517 // Derived class constructors (and their nested arrow functions and evals) 518 // use ThisBinding::DerivedConstructor, which ensures TDZ checks happen 519 // when accessing |this|. 520 if (si.scope().isDerivedClassConstructor()) { 521 thisBinding = ThisBinding::DerivedConstructor; 522 } else { 523 thisBinding = ThisBinding::Function; 524 } 525 526 return; 527 } 528 } 529 530 thisBinding = ThisBinding::Global; 531 } 532 533 void ScopeContext::computeInScope(const InputScope& enclosingScope) { 534 for (InputScopeIter si(enclosingScope); si; si++) { 535 if (si.kind() == ScopeKind::ClassBody) { 536 inClass = true; 537 } 538 539 if (si.kind() == ScopeKind::With) { 540 inWith = true; 541 } 542 } 543 } 544 545 void ScopeContext::cacheEnclosingScope(const InputScope& enclosingScope) { 546 if (enclosingScope.isNull()) { 547 return; 548 } 549 550 enclosingScopeEnvironmentChainLength = 551 enclosingScope.environmentChainLength(); 552 enclosingScopeKind = enclosingScope.kind(); 553 554 if (enclosingScopeKind == ScopeKind::Function) { 555 enclosingScopeIsArrow = enclosingScope.isArrow(); 556 } 557 558 enclosingScopeHasEnvironment = enclosingScope.hasEnvironment(); 559 560 #ifdef DEBUG 561 hasNonSyntacticScopeOnChain = 562 enclosingScope.hasOnChain(ScopeKind::NonSyntactic); 563 564 // This computes a general answer for the query "does the enclosing scope 565 // have a function scope that needs a home object?", but it's only asserted 566 // if the parser parses eval body that contains `super` that needs a home 567 // object. 568 for (InputScopeIter si(enclosingScope); si; si++) { 569 if (si.kind() == ScopeKind::Function) { 570 if (si.scope().isArrow()) { 571 continue; 572 } 573 if (si.scope().allowSuperProperty() && si.scope().needsHomeObject()) { 574 hasFunctionNeedsHomeObjectOnChain = true; 575 } 576 break; 577 } 578 } 579 #endif 580 581 // Pre-fill the scope cache by iterating over all the names. Stop iterating 582 // as soon as we find a scope which already has a filled scope cache. 583 AutoEnterOOMUnsafeRegion oomUnsafe; 584 for (InputScopeIter si(enclosingScope); si; si++) { 585 // If the current scope already exists, then there is no need to go deeper 586 // as the scope which are encoded after this one should already be present 587 // in the cache. 588 bool hasScopeCache = si.scope().match([&](auto& scope_ref) -> bool { 589 MOZ_ASSERT(scopeCache->canCacheFor(scope_ref)); 590 return scopeCache->lookupScope(scope_ref, scopeCacheGen); 591 }); 592 if (hasScopeCache) { 593 return; 594 } 595 596 bool hasEnv = si.hasSyntacticEnvironment(); 597 auto setCatchAll = [&](NameLocation loc) { 598 return si.scope().match([&](auto& scope_ref) { 599 using BindingMapPtr = decltype(scopeCache->createCacheFor(scope_ref)); 600 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref); 601 if (!bindingMapPtr) { 602 oomUnsafe.crash( 603 "ScopeContext::cacheEnclosingScope: scopeCache->createCacheFor"); 604 return; 605 } 606 607 bindingMapPtr->catchAll.emplace(loc); 608 }); 609 }; 610 auto createEmpty = [&]() { 611 return si.scope().match([&](auto& scope_ref) { 612 using BindingMapPtr = decltype(scopeCache->createCacheFor(scope_ref)); 613 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref); 614 if (!bindingMapPtr) { 615 oomUnsafe.crash( 616 "ScopeContext::cacheEnclosingScope: scopeCache->createCacheFor"); 617 return; 618 } 619 }); 620 }; 621 622 switch (si.kind()) { 623 case ScopeKind::Function: 624 if (hasEnv) { 625 if (si.scope().funHasExtensibleScope()) { 626 setCatchAll(NameLocation::Dynamic()); 627 return; 628 } 629 630 si.scope().match([&](auto& scope_ref) { 631 using BindingMapPtr = 632 decltype(scopeCache->createCacheFor(scope_ref)); 633 using Lookup = 634 typename std::remove_pointer_t<BindingMapPtr>::Lookup; 635 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref); 636 if (!bindingMapPtr) { 637 oomUnsafe.crash( 638 "ScopeContext::cacheEnclosingScope: " 639 "scopeCache->createCacheFor"); 640 return; 641 } 642 643 for (auto bi = InputBindingIter(scope_ref); bi; bi++) { 644 NameLocation loc = bi.nameLocation(); 645 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) { 646 continue; 647 } 648 auto ctxFreeKey = bi.name(); 649 GenericAtom ctxKey(scope_ref, ctxFreeKey); 650 Lookup ctxLookup(scope_ref, ctxKey); 651 if (!bindingMapPtr->hashMap.put(ctxLookup, ctxFreeKey, loc)) { 652 oomUnsafe.crash( 653 "ScopeContext::cacheEnclosingScope: bindingMapPtr->put"); 654 return; 655 } 656 } 657 }); 658 } else { 659 createEmpty(); 660 } 661 break; 662 663 case ScopeKind::StrictEval: 664 case ScopeKind::FunctionBodyVar: 665 case ScopeKind::Lexical: 666 case ScopeKind::NamedLambda: 667 case ScopeKind::StrictNamedLambda: 668 case ScopeKind::SimpleCatch: 669 case ScopeKind::Catch: 670 case ScopeKind::FunctionLexical: 671 case ScopeKind::ClassBody: 672 if (hasEnv) { 673 si.scope().match([&](auto& scope_ref) { 674 using BindingMapPtr = 675 decltype(scopeCache->createCacheFor(scope_ref)); 676 using Lookup = 677 typename std::remove_pointer_t<BindingMapPtr>::Lookup; 678 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref); 679 if (!bindingMapPtr) { 680 oomUnsafe.crash( 681 "ScopeContext::cacheEnclosingScope: " 682 "scopeCache->createCacheFor"); 683 return; 684 } 685 686 for (auto bi = InputBindingIter(scope_ref); bi; bi++) { 687 NameLocation loc = bi.nameLocation(); 688 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) { 689 continue; 690 } 691 auto ctxFreeKey = bi.name(); 692 GenericAtom ctxKey(scope_ref, ctxFreeKey); 693 Lookup ctxLookup(scope_ref, ctxKey); 694 if (!bindingMapPtr->hashMap.putNew(ctxLookup, ctxFreeKey, loc)) { 695 oomUnsafe.crash( 696 "ScopeContext::cacheEnclosingScope: bindingMapPtr->put"); 697 return; 698 } 699 } 700 }); 701 } else { 702 createEmpty(); 703 } 704 break; 705 706 case ScopeKind::Module: 707 // This case is used only when delazifying a function inside 708 // module. 709 // Initial compilation of module doesn't have enlcosing scope. 710 if (hasEnv) { 711 si.scope().match([&](auto& scope_ref) { 712 using BindingMapPtr = 713 decltype(scopeCache->createCacheFor(scope_ref)); 714 using Lookup = 715 typename std::remove_pointer_t<BindingMapPtr>::Lookup; 716 BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref); 717 if (!bindingMapPtr) { 718 oomUnsafe.crash( 719 "ScopeContext::cacheEnclosingScope: " 720 "scopeCache->createCacheFor"); 721 return; 722 } 723 724 for (auto bi = InputBindingIter(scope_ref); bi; bi++) { 725 // Imports are on the environment but are indirect 726 // bindings and must be accessed dynamically instead of 727 // using an EnvironmentCoordinate. 728 NameLocation loc = bi.nameLocation(); 729 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate && 730 loc.kind() != NameLocation::Kind::Import) { 731 continue; 732 } 733 auto ctxFreeKey = bi.name(); 734 GenericAtom ctxKey(scope_ref, ctxFreeKey); 735 Lookup ctxLookup(scope_ref, ctxKey); 736 if (!bindingMapPtr->hashMap.putNew(ctxLookup, ctxFreeKey, loc)) { 737 oomUnsafe.crash( 738 "ScopeContext::cacheEnclosingScope: bindingMapPtr->put"); 739 return; 740 } 741 } 742 }); 743 } else { 744 createEmpty(); 745 } 746 break; 747 748 case ScopeKind::Eval: 749 // As an optimization, if the eval doesn't have its own var 750 // environment and its immediate enclosing scope is a global 751 // scope, all accesses are global. 752 if (!hasEnv) { 753 ScopeKind kind = si.scope().enclosing().kind(); 754 if (kind == ScopeKind::Global || kind == ScopeKind::NonSyntactic) { 755 setCatchAll(NameLocation::Global(BindingKind::Var)); 756 return; 757 } 758 } 759 760 setCatchAll(NameLocation::Dynamic()); 761 return; 762 763 case ScopeKind::Global: 764 setCatchAll(NameLocation::Global(BindingKind::Var)); 765 return; 766 767 case ScopeKind::With: 768 case ScopeKind::NonSyntactic: 769 setCatchAll(NameLocation::Dynamic()); 770 return; 771 772 case ScopeKind::WasmInstance: 773 case ScopeKind::WasmFunction: 774 MOZ_CRASH("No direct eval inside wasm functions"); 775 } 776 } 777 778 MOZ_CRASH("Malformed scope chain"); 779 } 780 781 // Given an input scope, possibly refine this to a more precise scope. 782 // This is used during eval in the debugger to provide the appropriate scope and 783 // ThisBinding kind and environment, which is key to making private field eval 784 // work correctly. 785 // 786 // The trick here is that an eval may have a non-syntatic scope but nevertheless 787 // have an 'interesting' environment which can be traversed to find the 788 // appropriate scope the the eval to function as desired. See the diagram below. 789 // 790 // Eval Scope Eval Env Frame Env Frame Scope 791 // ============ ============= ========= ============= 792 // 793 // NonSyntactic 794 // | 795 // v 796 // null DebugEnvProxy LexicalScope 797 // | | 798 // v v 799 // DebugEnvProxy --> CallObj --> FunctionScope 800 // | | | 801 // v v v 802 // ... ... ... 803 // 804 InputScope ScopeContext::determineEffectiveScope(InputScope& scope, 805 JSObject* environment) { 806 MOZ_ASSERT(effectiveScopeHops == 0); 807 // If the scope-chain is non-syntactic, we may still determine a more precise 808 // effective-scope to use instead. 809 if (environment && scope.hasOnChain(ScopeKind::NonSyntactic)) { 810 JSObject* env = environment; 811 while (env) { 812 // Look at target of any DebugEnvironmentProxy, but be sure to use 813 // enclosingEnvironment() of the proxy itself. 814 JSObject* unwrapped = env; 815 if (env->is<DebugEnvironmentProxy>()) { 816 unwrapped = &env->as<DebugEnvironmentProxy>().environment(); 817 #ifdef DEBUG 818 enclosingEnvironmentIsDebugProxy_ = true; 819 #endif 820 } 821 822 if (unwrapped->is<CallObject>()) { 823 JSFunction* callee = &unwrapped->as<CallObject>().callee(); 824 return InputScope(callee->nonLazyScript()->bodyScope()); 825 } 826 827 env = env->enclosingEnvironment(); 828 effectiveScopeHops++; 829 } 830 } 831 832 return scope; 833 } 834 835 static uint32_t DepthOfNearestVarScopeForDirectEval(const InputScope& scope) { 836 uint32_t depth = 0; 837 if (scope.isNull()) { 838 return depth; 839 } 840 for (InputScopeIter si(scope); si; si++) { 841 depth++; 842 switch (si.scope().kind()) { 843 case ScopeKind::Function: 844 case ScopeKind::FunctionBodyVar: 845 case ScopeKind::Global: 846 case ScopeKind::NonSyntactic: 847 return depth; 848 default: 849 break; 850 } 851 } 852 return depth; 853 } 854 855 bool ScopeContext::cacheEnclosingScopeBindingForEval( 856 FrontendContext* fc, CompilationInput& input, 857 ParserAtomsTable& parserAtoms) { 858 enclosingLexicalBindingCache_.emplace(); 859 860 uint32_t varScopeDepth = 861 DepthOfNearestVarScopeForDirectEval(input.enclosingScope); 862 uint32_t depth = 0; 863 for (InputScopeIter si(input.enclosingScope); si; si++) { 864 bool success = si.scope().match([&](auto& scope_ref) { 865 for (auto bi = InputBindingIter(scope_ref); bi; bi++) { 866 switch (bi.kind()) { 867 case BindingKind::Let: { 868 // Annex B.3.5 allows redeclaring simple (non-destructured) 869 // catch parameters with var declarations. 870 bool annexB35Allowance = si.kind() == ScopeKind::SimpleCatch; 871 if (!annexB35Allowance) { 872 auto kind = ScopeKindIsCatch(si.kind()) 873 ? EnclosingLexicalBindingKind::CatchParameter 874 : EnclosingLexicalBindingKind::Let; 875 InputName binding(scope_ref, bi.name()); 876 if (!addToEnclosingLexicalBindingCache( 877 fc, parserAtoms, input.atomCache, binding, kind)) { 878 return false; 879 } 880 } 881 break; 882 } 883 884 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 885 // TODO: Optimize cache population for `using` bindings. (Bug 1899502) 886 case BindingKind::Using: 887 break; 888 #endif 889 case BindingKind::Const: { 890 InputName binding(scope_ref, bi.name()); 891 if (!addToEnclosingLexicalBindingCache( 892 fc, parserAtoms, input.atomCache, binding, 893 EnclosingLexicalBindingKind::Const)) { 894 return false; 895 } 896 break; 897 } 898 899 case BindingKind::Synthetic: { 900 InputName binding(scope_ref, bi.name()); 901 if (!addToEnclosingLexicalBindingCache( 902 fc, parserAtoms, input.atomCache, binding, 903 EnclosingLexicalBindingKind::Synthetic)) { 904 return false; 905 } 906 break; 907 } 908 909 case BindingKind::PrivateMethod: { 910 InputName binding(scope_ref, bi.name()); 911 if (!addToEnclosingLexicalBindingCache( 912 fc, parserAtoms, input.atomCache, binding, 913 EnclosingLexicalBindingKind::PrivateMethod)) { 914 return false; 915 } 916 break; 917 } 918 919 case BindingKind::Import: 920 case BindingKind::FormalParameter: 921 case BindingKind::Var: 922 case BindingKind::NamedLambdaCallee: 923 break; 924 } 925 } 926 return true; 927 }); 928 if (!success) { 929 return false; 930 } 931 932 if (++depth == varScopeDepth) { 933 break; 934 } 935 } 936 937 return true; 938 } 939 940 bool ScopeContext::addToEnclosingLexicalBindingCache( 941 FrontendContext* fc, ParserAtomsTable& parserAtoms, 942 CompilationAtomCache& atomCache, InputName& name, 943 EnclosingLexicalBindingKind kind) { 944 TaggedParserAtomIndex parserName = 945 name.internInto(fc, parserAtoms, atomCache); 946 if (!parserName) { 947 return false; 948 } 949 950 // Same lexical binding can appear multiple times across scopes. 951 // 952 // enclosingLexicalBindingCache_ map is used for detecting conflicting 953 // `var` binding, and inner binding should be reported in the error. 954 // 955 // cacheEnclosingScopeBindingForEval iterates from inner scope, and 956 // inner-most binding is added to the map first. 957 // 958 // Do not overwrite the value with outer bindings. 959 auto p = enclosingLexicalBindingCache_->lookupForAdd(parserName); 960 if (!p) { 961 if (!enclosingLexicalBindingCache_->add(p, parserName, kind)) { 962 ReportOutOfMemory(fc); 963 return false; 964 } 965 } 966 967 return true; 968 } 969 970 static bool IsPrivateField(Scope*, JSAtom* atom) { 971 MOZ_ASSERT(atom->length() > 0); 972 973 JS::AutoCheckCannotGC nogc; 974 if (atom->hasLatin1Chars()) { 975 return atom->latin1Chars(nogc)[0] == '#'; 976 } 977 978 return atom->twoByteChars(nogc)[0] == '#'; 979 } 980 981 static bool IsPrivateField(ScopeStencilRef& scope, TaggedParserAtomIndex atom) { 982 if (atom.isParserAtomIndex()) { 983 const CompilationStencil& context = *scope.context(); 984 ParserAtom* parserAtom = context.parserAtomData[atom.toParserAtomIndex()]; 985 return parserAtom->isPrivateName(); 986 } 987 988 #ifdef DEBUG 989 if (atom.isWellKnownAtomId()) { 990 const auto& info = GetWellKnownAtomInfo(atom.toWellKnownAtomId()); 991 // #constructor is a well-known term, but it is invalid private name. 992 MOZ_ASSERT(!(info.length > 1 && info.content[0] == '#')); 993 } else if (atom.isLength2StaticParserString()) { 994 char content[2]; 995 ParserAtomsTable::getLength2Content(atom.toLength2StaticParserString(), 996 content); 997 // # character is not part of the allowed character of static strings. 998 MOZ_ASSERT(content[0] != '#'); 999 } 1000 #endif 1001 1002 return false; 1003 } 1004 1005 static bool IsPrivateField(const FakeStencilGlobalScope&, 1006 TaggedParserAtomIndex) { 1007 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No private fields on empty global."); 1008 } 1009 1010 bool ScopeContext::cachePrivateFieldsForEval(FrontendContext* fc, 1011 CompilationInput& input, 1012 JSObject* enclosingEnvironment, 1013 const InputScope& effectiveScope, 1014 ParserAtomsTable& parserAtoms) { 1015 effectiveScopePrivateFieldCache_.emplace(); 1016 1017 // We compute an environment coordinate relative to the effective scope 1018 // environment. In order to safely consume these environment coordinates, 1019 // we re-map them to include the hops to get the to the effective scope: 1020 // see EmitterScope::lookupPrivate 1021 uint32_t hops = effectiveScopeHops; 1022 for (InputScopeIter si(effectiveScope); si; si++) { 1023 if (si.scope().kind() == ScopeKind::ClassBody) { 1024 uint32_t slots = 0; 1025 bool success = si.scope().match([&](auto& scope_ref) { 1026 for (auto bi = InputBindingIter(scope_ref); bi; bi++) { 1027 if (bi.kind() == BindingKind::PrivateMethod || 1028 (bi.kind() == BindingKind::Synthetic && 1029 IsPrivateField(scope_ref, bi.name()))) { 1030 InputName binding(scope_ref, bi.name()); 1031 auto parserName = 1032 binding.internInto(fc, parserAtoms, input.atomCache); 1033 if (!parserName) { 1034 return false; 1035 } 1036 1037 NameLocation loc = NameLocation::DebugEnvironmentCoordinate( 1038 bi.kind(), hops, slots); 1039 1040 if (!effectiveScopePrivateFieldCache_->put(parserName, loc)) { 1041 ReportOutOfMemory(fc); 1042 return false; 1043 } 1044 } 1045 slots++; 1046 } 1047 return true; 1048 }); 1049 if (!success) { 1050 return false; 1051 } 1052 } 1053 1054 // Hops is only consumed by GetAliasedDebugVar, which uses this to 1055 // traverse the debug environment chain. See the [SMDOC] for Debug 1056 // Environment Chain, which explains why we don't check for 1057 // isEnvironment when computing hops here (basically, debug proxies 1058 // pretend all scopes have environments, even if they were actually 1059 // optimized out). 1060 hops++; 1061 } 1062 1063 return true; 1064 } 1065 1066 #ifdef DEBUG 1067 static bool NameIsOnEnvironment(FrontendContext* fc, 1068 ParserAtomsTable& parserAtoms, 1069 CompilationAtomCache& atomCache, 1070 InputScope& scope, TaggedParserAtomIndex name) { 1071 JSAtom* jsname = nullptr; 1072 return scope.match([&](auto& scope_ref) { 1073 if (std::is_same_v<decltype(scope_ref), FakeStencilGlobalScope&>) { 1074 // This condition is added to handle the FakeStencilGlobalScope which is 1075 // used to emulate the global object when delazifying while executing, and 1076 // which is not provided by the Stencil. 1077 return true; 1078 } 1079 for (auto bi = InputBindingIter(scope_ref); bi; bi++) { 1080 // If found, the name must already be on the environment or an import, 1081 // or else there is a bug in the closed-over name analysis in the 1082 // Parser. 1083 InputName binding(scope_ref, bi.name()); 1084 if (binding.isEqualTo(fc, parserAtoms, atomCache, name, &jsname)) { 1085 BindingLocation::Kind kind = bi.location().kind(); 1086 1087 if (bi.hasArgumentSlot()) { 1088 // The following is equivalent to 1089 // functionScope.script()->functionAllowsParameterRedeclaration() 1090 if (scope.hasMappedArgsObj()) { 1091 // Check for duplicate positional formal parameters. 1092 using InputBindingIter = decltype(bi); 1093 for (InputBindingIter bi2(bi); bi2 && bi2.hasArgumentSlot(); 1094 bi2++) { 1095 InputName binding2(scope_ref, bi2.name()); 1096 if (binding2.isEqualTo(fc, parserAtoms, atomCache, name, 1097 &jsname)) { 1098 kind = bi2.location().kind(); 1099 } 1100 } 1101 } 1102 } 1103 1104 return kind == BindingLocation::Kind::Global || 1105 kind == BindingLocation::Kind::Environment || 1106 kind == BindingLocation::Kind::Import; 1107 } 1108 } 1109 1110 // If not found, assume it's on the global or dynamically accessed. 1111 return true; 1112 }); 1113 } 1114 #endif 1115 1116 NameLocation ScopeContext::searchInEnclosingScope(FrontendContext* fc, 1117 CompilationInput& input, 1118 ParserAtomsTable& parserAtoms, 1119 TaggedParserAtomIndex name) { 1120 MOZ_ASSERT(input.target == 1121 CompilationInput::CompilationTarget::Delazification || 1122 input.target == CompilationInput::CompilationTarget::Eval); 1123 1124 MOZ_ASSERT(scopeCache); 1125 if (scopeCacheGen != scopeCache->getCurrentGeneration()) { 1126 return searchInEnclosingScopeNoCache(fc, input, parserAtoms, name); 1127 } 1128 1129 #ifdef DEBUG 1130 // Catch assertion failures in the NoCache variant before looking at the 1131 // cached content. 1132 NameLocation expect = 1133 searchInEnclosingScopeNoCache(fc, input, parserAtoms, name); 1134 #endif 1135 1136 NameLocation found = 1137 searchInEnclosingScopeWithCache(fc, input, parserAtoms, name); 1138 MOZ_ASSERT(expect == found); 1139 return found; 1140 } 1141 1142 NameLocation ScopeContext::searchInEnclosingScopeWithCache( 1143 FrontendContext* fc, CompilationInput& input, ParserAtomsTable& parserAtoms, 1144 TaggedParserAtomIndex name) { 1145 MOZ_ASSERT(input.target == 1146 CompilationInput::CompilationTarget::Delazification || 1147 input.target == CompilationInput::CompilationTarget::Eval); 1148 1149 // Generic atom of the looked up name. 1150 GenericAtom genName(fc, parserAtoms, input.atomCache, name); 1151 mozilla::Maybe<NameLocation> found; 1152 1153 // Number of enclosing scope we walked over. 1154 uint16_t hops = 0; 1155 1156 for (InputScopeIter si(input.enclosingScope); si; si++) { 1157 MOZ_ASSERT(NameIsOnEnvironment(fc, parserAtoms, input.atomCache, si.scope(), 1158 name)); 1159 1160 // If the result happens to be in the cached content of the scope that we 1161 // are iterating over, then return it. 1162 si.scope().match([&](auto& scope_ref) { 1163 using BindingMapPtr = 1164 decltype(scopeCache->lookupScope(scope_ref, scopeCacheGen)); 1165 BindingMapPtr bindingMapPtr = 1166 scopeCache->lookupScope(scope_ref, scopeCacheGen); 1167 MOZ_ASSERT(bindingMapPtr); 1168 1169 auto& bindingMap = *bindingMapPtr; 1170 if (bindingMap.catchAll.isSome()) { 1171 found = bindingMap.catchAll; 1172 return; 1173 } 1174 1175 // The scope_ref is given as argument to know where to lookup the key 1176 // index of the hash table if the names have to be compared. 1177 using Lookup = typename std::remove_pointer_t<BindingMapPtr>::Lookup; 1178 Lookup ctxName(scope_ref, genName); 1179 auto ptr = bindingMap.hashMap.lookup(ctxName); 1180 if (!ptr) { 1181 return; 1182 } 1183 1184 found.emplace(ptr->value()); 1185 }); 1186 1187 if (found.isSome()) { 1188 // Cached entries do not store the number of hops, as it might be reused 1189 // by multiple inner functions, which might different number of hops. 1190 found = found.map([&hops](NameLocation loc) { 1191 if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) { 1192 return loc; 1193 } 1194 return loc.addHops(hops); 1195 }); 1196 return found.value(); 1197 } 1198 1199 bool hasEnv = si.hasSyntacticEnvironment(); 1200 1201 if (hasEnv) { 1202 MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1); 1203 hops++; 1204 } 1205 } 1206 1207 MOZ_CRASH("Malformed scope chain"); 1208 } 1209 1210 NameLocation ScopeContext::searchInEnclosingScopeNoCache( 1211 FrontendContext* fc, CompilationInput& input, ParserAtomsTable& parserAtoms, 1212 TaggedParserAtomIndex name) { 1213 MOZ_ASSERT(input.target == 1214 CompilationInput::CompilationTarget::Delazification || 1215 input.target == CompilationInput::CompilationTarget::Eval); 1216 1217 // Cached JSAtom equivalent of the TaggedParserAtomIndex `name` argument. 1218 JSAtom* jsname = nullptr; 1219 1220 // NameLocation which contains relative locations to access `name`. 1221 mozilla::Maybe<NameLocation> result; 1222 1223 // Number of enclosing scope we walked over. 1224 uint16_t hops = 0; 1225 1226 for (InputScopeIter si(input.enclosingScope); si; si++) { 1227 MOZ_ASSERT(NameIsOnEnvironment(fc, parserAtoms, input.atomCache, si.scope(), 1228 name)); 1229 1230 bool hasEnv = si.hasSyntacticEnvironment(); 1231 switch (si.kind()) { 1232 case ScopeKind::Function: 1233 if (hasEnv) { 1234 if (si.scope().funHasExtensibleScope()) { 1235 return NameLocation::Dynamic(); 1236 } 1237 1238 si.scope().match([&](auto& scope_ref) { 1239 for (auto bi = InputBindingIter(scope_ref); bi; bi++) { 1240 InputName binding(scope_ref, bi.name()); 1241 if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name, 1242 &jsname)) { 1243 continue; 1244 } 1245 1246 BindingLocation bindLoc = bi.location(); 1247 // hasMappedArgsObj == script.functionAllowsParameterRedeclaration 1248 if (bi.hasArgumentSlot() && si.scope().hasMappedArgsObj()) { 1249 // Check for duplicate positional formal parameters. 1250 using InputBindingIter = decltype(bi); 1251 for (InputBindingIter bi2(bi); bi2 && bi2.hasArgumentSlot(); 1252 bi2++) { 1253 if (bi.name() == bi2.name()) { 1254 bindLoc = bi2.location(); 1255 } 1256 } 1257 } 1258 1259 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment); 1260 result.emplace(NameLocation::EnvironmentCoordinate( 1261 bi.kind(), hops, bindLoc.slot())); 1262 return; 1263 } 1264 }); 1265 } 1266 break; 1267 1268 case ScopeKind::StrictEval: 1269 case ScopeKind::FunctionBodyVar: 1270 case ScopeKind::Lexical: 1271 case ScopeKind::NamedLambda: 1272 case ScopeKind::StrictNamedLambda: 1273 case ScopeKind::SimpleCatch: 1274 case ScopeKind::Catch: 1275 case ScopeKind::FunctionLexical: 1276 case ScopeKind::ClassBody: 1277 if (hasEnv) { 1278 si.scope().match([&](auto& scope_ref) { 1279 for (auto bi = InputBindingIter(scope_ref); bi; bi++) { 1280 InputName binding(scope_ref, bi.name()); 1281 if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name, 1282 &jsname)) { 1283 continue; 1284 } 1285 1286 // The name must already have been marked as closed 1287 // over. If this assertion is hit, there is a bug in the 1288 // name analysis. 1289 BindingLocation bindLoc = bi.location(); 1290 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment); 1291 result.emplace(NameLocation::EnvironmentCoordinate( 1292 bi.kind(), hops, bindLoc.slot())); 1293 return; 1294 } 1295 }); 1296 } 1297 break; 1298 1299 case ScopeKind::Module: 1300 // This case is used only when delazifying a function inside 1301 // module. 1302 // Initial compilation of module doesn't have enlcosing scope. 1303 if (hasEnv) { 1304 si.scope().match([&](auto& scope_ref) { 1305 for (auto bi = InputBindingIter(scope_ref); bi; bi++) { 1306 InputName binding(scope_ref, bi.name()); 1307 if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name, 1308 &jsname)) { 1309 continue; 1310 } 1311 1312 BindingLocation bindLoc = bi.location(); 1313 1314 // Imports are on the environment but are indirect 1315 // bindings and must be accessed dynamically instead of 1316 // using an EnvironmentCoordinate. 1317 if (bindLoc.kind() == BindingLocation::Kind::Import) { 1318 MOZ_ASSERT(si.kind() == ScopeKind::Module); 1319 result.emplace(NameLocation::Import()); 1320 return; 1321 } 1322 1323 MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment); 1324 result.emplace(NameLocation::EnvironmentCoordinate( 1325 bi.kind(), hops, bindLoc.slot())); 1326 return; 1327 } 1328 }); 1329 } 1330 break; 1331 1332 case ScopeKind::Eval: 1333 // As an optimization, if the eval doesn't have its own var 1334 // environment and its immediate enclosing scope is a global 1335 // scope, all accesses are global. 1336 if (!hasEnv) { 1337 ScopeKind kind = si.scope().enclosing().kind(); 1338 if (kind == ScopeKind::Global || kind == ScopeKind::NonSyntactic) { 1339 return NameLocation::Global(BindingKind::Var); 1340 } 1341 } 1342 return NameLocation::Dynamic(); 1343 1344 case ScopeKind::Global: 1345 return NameLocation::Global(BindingKind::Var); 1346 1347 case ScopeKind::With: 1348 case ScopeKind::NonSyntactic: 1349 return NameLocation::Dynamic(); 1350 1351 case ScopeKind::WasmInstance: 1352 case ScopeKind::WasmFunction: 1353 MOZ_CRASH("No direct eval inside wasm functions"); 1354 } 1355 1356 if (result.isSome()) { 1357 return result.value(); 1358 } 1359 1360 if (hasEnv) { 1361 MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1); 1362 hops++; 1363 } 1364 } 1365 1366 MOZ_CRASH("Malformed scope chain"); 1367 } 1368 1369 mozilla::Maybe<ScopeContext::EnclosingLexicalBindingKind> 1370 ScopeContext::lookupLexicalBindingInEnclosingScope(TaggedParserAtomIndex name) { 1371 auto p = enclosingLexicalBindingCache_->lookup(name); 1372 if (!p) { 1373 return mozilla::Nothing(); 1374 } 1375 1376 return mozilla::Some(p->value()); 1377 } 1378 1379 bool ScopeContext::effectiveScopePrivateFieldCacheHas( 1380 TaggedParserAtomIndex name) { 1381 return effectiveScopePrivateFieldCache_->has(name); 1382 } 1383 1384 mozilla::Maybe<NameLocation> ScopeContext::getPrivateFieldLocation( 1385 TaggedParserAtomIndex name) { 1386 // The locations returned by this method are only valid for 1387 // traversing debug environments. 1388 // 1389 // See the comment in cachePrivateFieldsForEval 1390 MOZ_ASSERT(enclosingEnvironmentIsDebugProxy_); 1391 auto p = effectiveScopePrivateFieldCache_->lookup(name); 1392 if (!p) { 1393 return mozilla::Nothing(); 1394 } 1395 return mozilla::Some(p->value()); 1396 } 1397 1398 bool CompilationInput::initScriptSource(FrontendContext* fc) { 1399 source = do_AddRef(fc->getAllocator()->new_<ScriptSource>()); 1400 if (!source) { 1401 return false; 1402 } 1403 1404 return source->initFromOptions(fc, options); 1405 } 1406 1407 bool CompilationInput::initForStandaloneFunctionInNonSyntacticScope( 1408 FrontendContext* fc, Handle<Scope*> functionEnclosingScope) { 1409 MOZ_ASSERT(!functionEnclosingScope->as<GlobalScope>().isSyntactic()); 1410 1411 target = CompilationTarget::StandaloneFunctionInNonSyntacticScope; 1412 if (!initScriptSource(fc)) { 1413 return false; 1414 } 1415 enclosingScope = InputScope(functionEnclosingScope); 1416 return true; 1417 } 1418 1419 FunctionSyntaxKind CompilationInput::functionSyntaxKind() const { 1420 if (functionFlags().isClassConstructor()) { 1421 if (functionFlags().hasBaseScript() && isDerivedClassConstructor()) { 1422 return FunctionSyntaxKind::DerivedClassConstructor; 1423 } 1424 return FunctionSyntaxKind::ClassConstructor; 1425 } 1426 if (functionFlags().isMethod()) { 1427 if (functionFlags().hasBaseScript() && isSyntheticFunction()) { 1428 // return FunctionSyntaxKind::FieldInitializer; 1429 MOZ_ASSERT_UNREACHABLE( 1430 "Lazy parsing of class field initializers not supported (yet)"); 1431 } 1432 return FunctionSyntaxKind::Method; 1433 } 1434 if (functionFlags().isGetter()) { 1435 return FunctionSyntaxKind::Getter; 1436 } 1437 if (functionFlags().isSetter()) { 1438 return FunctionSyntaxKind::Setter; 1439 } 1440 if (functionFlags().isArrow()) { 1441 return FunctionSyntaxKind::Arrow; 1442 } 1443 return FunctionSyntaxKind::Statement; 1444 } 1445 1446 bool CompilationInput::internExtraBindings(FrontendContext* fc, 1447 ParserAtomsTable& parserAtoms) { 1448 MOZ_ASSERT(hasExtraBindings()); 1449 1450 for (auto& bindingInfo : *maybeExtraBindings_) { 1451 if (bindingInfo.isShadowed) { 1452 continue; 1453 } 1454 1455 const char* chars = bindingInfo.nameChars.get(); 1456 auto index = parserAtoms.internUtf8( 1457 fc, reinterpret_cast<const mozilla::Utf8Unit*>(chars), strlen(chars)); 1458 if (!index) { 1459 return false; 1460 } 1461 1462 bindingInfo.nameIndex = index; 1463 } 1464 1465 return true; 1466 } 1467 1468 void InputScope::trace(JSTracer* trc) { 1469 using ScopePtr = Scope*; 1470 if (scope_.is<ScopePtr>()) { 1471 ScopePtr* ptrAddr = &scope_.as<ScopePtr>(); 1472 TraceNullableRoot(trc, ptrAddr, "compilation-input-scope"); 1473 } 1474 } 1475 1476 void InputScript::trace(JSTracer* trc) { 1477 using ScriptPtr = BaseScript*; 1478 if (script_.is<ScriptPtr>()) { 1479 ScriptPtr* ptrAddr = &script_.as<ScriptPtr>(); 1480 TraceNullableRoot(trc, ptrAddr, "compilation-input-lazy"); 1481 } 1482 } 1483 1484 void CompilationInput::trace(JSTracer* trc) { 1485 atomCache.trace(trc); 1486 lazy_.trace(trc); 1487 enclosingScope.trace(trc); 1488 } 1489 1490 bool CompilationSyntaxParseCache::init(FrontendContext* fc, LifoAlloc& alloc, 1491 ParserAtomsTable& parseAtoms, 1492 CompilationAtomCache& atomCache, 1493 const InputScript& lazy) { 1494 if (!copyFunctionInfo(fc, parseAtoms, atomCache, lazy)) { 1495 return false; 1496 } 1497 bool success = lazy.raw().match([&](auto& ref) { 1498 if (!copyScriptInfo(fc, alloc, parseAtoms, atomCache, ref)) { 1499 return false; 1500 } 1501 if (!copyClosedOverBindings(fc, alloc, parseAtoms, atomCache, ref)) { 1502 return false; 1503 } 1504 return true; 1505 }); 1506 if (!success) { 1507 return false; 1508 } 1509 #ifdef DEBUG 1510 isInitialized = true; 1511 #endif 1512 return true; 1513 } 1514 1515 bool CompilationSyntaxParseCache::copyFunctionInfo( 1516 FrontendContext* fc, ParserAtomsTable& parseAtoms, 1517 CompilationAtomCache& atomCache, const InputScript& lazy) { 1518 InputName name = lazy.displayAtom(); 1519 if (!name.isNull()) { 1520 displayAtom_ = name.internInto(fc, parseAtoms, atomCache); 1521 if (!displayAtom_) { 1522 return false; 1523 } 1524 } 1525 1526 funExtra_.immutableFlags = lazy.immutableFlags(); 1527 funExtra_.extent = lazy.extent(); 1528 if (funExtra_.useMemberInitializers()) { 1529 funExtra_.setMemberInitializers(lazy.getMemberInitializers()); 1530 } 1531 1532 return true; 1533 } 1534 1535 bool CompilationSyntaxParseCache::copyScriptInfo( 1536 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms, 1537 CompilationAtomCache& atomCache, BaseScript* lazy) { 1538 using GCThingsSpan = mozilla::Span<TaggedScriptThingIndex>; 1539 using ScriptDataSpan = mozilla::Span<ScriptStencil>; 1540 using ScriptExtraSpan = mozilla::Span<ScriptStencilExtra>; 1541 cachedGCThings_ = GCThingsSpan(nullptr); 1542 cachedScriptData_ = ScriptDataSpan(nullptr); 1543 cachedScriptExtra_ = ScriptExtraSpan(nullptr); 1544 1545 auto gcthings = lazy->gcthings(); 1546 size_t length = gcthings.Length(); 1547 if (length == 0) { 1548 return true; 1549 } 1550 1551 // Reduce the length to the first element which is not a function. 1552 for (size_t i = 0; i < length; i++) { 1553 gc::Cell* cell = gcthings[i].asCell(); 1554 if (!cell || !cell->is<JSObject>()) { 1555 length = i; 1556 break; 1557 } 1558 MOZ_ASSERT(cell->as<JSObject>()->is<JSFunction>()); 1559 } 1560 1561 TaggedScriptThingIndex* gcThingsData = 1562 alloc.newArrayUninitialized<TaggedScriptThingIndex>(length); 1563 ScriptStencil* scriptData = 1564 alloc.newArrayUninitialized<ScriptStencil>(length); 1565 ScriptStencilExtra* scriptExtra = 1566 alloc.newArrayUninitialized<ScriptStencilExtra>(length); 1567 if (!gcThingsData || !scriptData || !scriptExtra) { 1568 ReportOutOfMemory(fc); 1569 return false; 1570 } 1571 1572 for (size_t i = 0; i < length; i++) { 1573 gc::Cell* cell = gcthings[i].asCell(); 1574 JSFunction* fun = &cell->as<JSObject>()->as<JSFunction>(); 1575 gcThingsData[i] = TaggedScriptThingIndex(ScriptIndex(i)); 1576 new (mozilla::KnownNotNull, &scriptData[i]) ScriptStencil(); 1577 ScriptStencil& data = scriptData[i]; 1578 new (mozilla::KnownNotNull, &scriptExtra[i]) ScriptStencilExtra(); 1579 ScriptStencilExtra& extra = scriptExtra[i]; 1580 1581 if (fun->fullDisplayAtom()) { 1582 TaggedParserAtomIndex displayAtom = 1583 parseAtoms.internJSAtom(fc, atomCache, fun->fullDisplayAtom()); 1584 if (!displayAtom) { 1585 return false; 1586 } 1587 data.functionAtom = displayAtom; 1588 } 1589 data.functionFlags = fun->flags(); 1590 1591 BaseScript* lazy = fun->baseScript(); 1592 extra.immutableFlags = lazy->immutableFlags(); 1593 extra.extent = lazy->extent(); 1594 1595 // Info derived from parent compilation should not be set yet for our inner 1596 // lazy functions. Instead that info will be updated when we finish our 1597 // compilation. 1598 MOZ_ASSERT(lazy->hasEnclosingScript()); 1599 } 1600 1601 cachedGCThings_ = GCThingsSpan(gcThingsData, length); 1602 cachedScriptData_ = ScriptDataSpan(scriptData, length); 1603 cachedScriptExtra_ = ScriptExtraSpan(scriptExtra, length); 1604 return true; 1605 } 1606 1607 bool CompilationSyntaxParseCache::copyScriptInfo( 1608 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms, 1609 CompilationAtomCache& atomCache, const ScriptStencilRef& lazy) { 1610 using GCThingsSpan = mozilla::Span<TaggedScriptThingIndex>; 1611 using ScriptDataSpan = mozilla::Span<ScriptStencil>; 1612 using ScriptExtraSpan = mozilla::Span<ScriptStencilExtra>; 1613 cachedGCThings_ = GCThingsSpan(nullptr); 1614 cachedScriptData_ = ScriptDataSpan(nullptr); 1615 cachedScriptExtra_ = ScriptExtraSpan(nullptr); 1616 1617 // We are only interested in the functions within the gcthings. The initial 1618 // stencil is already aware of all inner functions, and the script indexes can 1619 // help build additional ScriptStencilRef. 1620 auto gcThings = lazy.gcThingsFromInitial(); 1621 size_t length = gcThings.Length(); 1622 if (length == 0) { 1623 return true; 1624 } 1625 1626 // Reduce the length to the first element which is not a function. 1627 for (size_t i = 0; i < length; i++) { 1628 if (!gcThings[i].isFunction()) { 1629 length = i; 1630 break; 1631 } 1632 } 1633 1634 TaggedScriptThingIndex* gcThingsData = 1635 alloc.newArrayUninitialized<TaggedScriptThingIndex>(length); 1636 ScriptStencil* scriptData = 1637 alloc.newArrayUninitialized<ScriptStencil>(length); 1638 ScriptStencilExtra* scriptExtra = 1639 alloc.newArrayUninitialized<ScriptStencilExtra>(length); 1640 if (!gcThingsData || !scriptData || !scriptExtra) { 1641 ReportOutOfMemory(fc); 1642 return false; 1643 } 1644 1645 for (size_t i = 0; i < length; i++) { 1646 // The gcThing array is taken out of the initial stencil, thus scriptIndex 1647 // are valid for the initial stencil only. 1648 ScriptIndex innerIndex = gcThings[i].toFunction(); 1649 ScriptStencilRef inner{lazy.stencils_, innerIndex}; 1650 1651 // scriptData is extracted from the initial stencil as we are about to 1652 // compile the enclosing script. 1653 const ScriptStencil& srcData = inner.scriptDataFromInitial(); 1654 1655 gcThingsData[i] = TaggedScriptThingIndex(ScriptIndex(i)); 1656 new (mozilla::KnownNotNull, &scriptData[i]) ScriptStencil(); 1657 ScriptStencil& data = scriptData[i]; 1658 new (mozilla::KnownNotNull, &scriptExtra[i]) ScriptStencilExtra(); 1659 ScriptStencilExtra& extra = scriptExtra[i]; 1660 1661 InputName name{inner.topLevelScript(), srcData.functionAtom}; 1662 if (!name.isNull()) { 1663 auto displayAtom = name.internInto(fc, parseAtoms, atomCache); 1664 if (!displayAtom) { 1665 return false; 1666 } 1667 data.functionAtom = displayAtom; 1668 } 1669 data.functionFlags = srcData.functionFlags; 1670 1671 extra = inner.scriptExtra(); 1672 } 1673 1674 cachedGCThings_ = GCThingsSpan(gcThingsData, length); 1675 cachedScriptData_ = ScriptDataSpan(scriptData, length); 1676 cachedScriptExtra_ = ScriptExtraSpan(scriptExtra, length); 1677 return true; 1678 } 1679 1680 bool CompilationSyntaxParseCache::copyClosedOverBindings( 1681 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms, 1682 CompilationAtomCache& atomCache, BaseScript* lazy) { 1683 using ClosedOverBindingsSpan = mozilla::Span<TaggedParserAtomIndex>; 1684 closedOverBindings_ = ClosedOverBindingsSpan(nullptr); 1685 1686 // The gcthings() array contains the inner function list followed by the 1687 // closed-over bindings data. Skip the inner function list, as it is already 1688 // cached in cachedGCThings_. See also: BaseScript::CreateLazy. 1689 size_t start = cachedGCThings_.Length(); 1690 auto gcthings = lazy->gcthings(); 1691 size_t length = gcthings.Length(); 1692 MOZ_ASSERT(start <= length); 1693 if (length - start == 0) { 1694 return true; 1695 } 1696 1697 TaggedParserAtomIndex* closedOverBindings = 1698 alloc.newArrayUninitialized<TaggedParserAtomIndex>(length - start); 1699 if (!closedOverBindings) { 1700 ReportOutOfMemory(fc); 1701 return false; 1702 } 1703 1704 for (size_t i = start; i < length; i++) { 1705 gc::Cell* cell = gcthings[i].asCell(); 1706 if (!cell) { 1707 closedOverBindings[i - start] = TaggedParserAtomIndex::null(); 1708 continue; 1709 } 1710 1711 MOZ_ASSERT(cell->as<JSString>()->isAtom()); 1712 1713 auto name = static_cast<JSAtom*>(cell); 1714 auto parserAtom = parseAtoms.internJSAtom(fc, atomCache, name); 1715 if (!parserAtom) { 1716 return false; 1717 } 1718 1719 closedOverBindings[i - start] = parserAtom; 1720 } 1721 1722 closedOverBindings_ = 1723 ClosedOverBindingsSpan(closedOverBindings, length - start); 1724 return true; 1725 } 1726 1727 bool CompilationSyntaxParseCache::copyClosedOverBindings( 1728 FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms, 1729 CompilationAtomCache& atomCache, const ScriptStencilRef& lazy) { 1730 using ClosedOverBindingsSpan = mozilla::Span<TaggedParserAtomIndex>; 1731 closedOverBindings_ = ClosedOverBindingsSpan(nullptr); 1732 1733 // The gcthings array contains the inner function list followed by the 1734 // closed-over bindings data. Skip the inner function list, as it is already 1735 // cached in cachedGCThings_. See also: BaseScript::CreateLazy. 1736 auto gcthings = lazy.gcThingsFromInitial(); 1737 size_t length = gcthings.Length(); 1738 size_t start = cachedGCThings_.Length(); 1739 MOZ_ASSERT(start <= length); 1740 if (length - start == 0) { 1741 return true; 1742 } 1743 length -= start; 1744 1745 // Atoms from the lazy.context (CompilationStencil) are not registered in the 1746 // the parseAtoms table. Thus we create a new span which will contain all the 1747 // interned atoms. 1748 TaggedParserAtomIndex* closedOverBindings = 1749 alloc.newArrayUninitialized<TaggedParserAtomIndex>(length); 1750 if (!closedOverBindings) { 1751 ReportOutOfMemory(fc); 1752 return false; 1753 } 1754 1755 for (size_t i = 0; i < length; i++) { 1756 auto gcThing = gcthings[i + start]; 1757 if (gcThing.isNull()) { 1758 closedOverBindings[i] = TaggedParserAtomIndex::null(); 1759 continue; 1760 } 1761 1762 MOZ_ASSERT(gcThing.isAtom()); 1763 InputName name(lazy.topLevelScript(), gcThing.toAtom()); 1764 auto parserAtom = name.internInto(fc, parseAtoms, atomCache); 1765 if (!parserAtom) { 1766 return false; 1767 } 1768 1769 closedOverBindings[i] = parserAtom; 1770 } 1771 1772 closedOverBindings_ = ClosedOverBindingsSpan(closedOverBindings, length); 1773 return true; 1774 } 1775 1776 template <typename T> 1777 PreAllocateableGCArray<T>::~PreAllocateableGCArray() { 1778 if (elems_) { 1779 js_free(elems_); 1780 elems_ = nullptr; 1781 } 1782 } 1783 1784 template <typename T> 1785 bool PreAllocateableGCArray<T>::allocate(size_t length) { 1786 MOZ_ASSERT(empty()); 1787 1788 length_ = length; 1789 1790 if (isInline()) { 1791 inlineElem_ = nullptr; 1792 return true; 1793 } 1794 1795 elems_ = reinterpret_cast<T*>(js_calloc(sizeof(T) * length_)); 1796 if (!elems_) { 1797 return false; 1798 } 1799 1800 return true; 1801 } 1802 1803 template <typename T> 1804 bool PreAllocateableGCArray<T>::allocateWith(T init, size_t length) { 1805 MOZ_ASSERT(empty()); 1806 1807 length_ = length; 1808 1809 if (isInline()) { 1810 inlineElem_ = init; 1811 return true; 1812 } 1813 1814 elems_ = reinterpret_cast<T*>(js_malloc(sizeof(T) * length_)); 1815 if (!elems_) { 1816 return false; 1817 } 1818 1819 std::fill(elems_, elems_ + length_, init); 1820 return true; 1821 } 1822 1823 template <typename T> 1824 void PreAllocateableGCArray<T>::steal(Preallocated&& buffer) { 1825 MOZ_ASSERT(empty()); 1826 1827 length_ = buffer.length_; 1828 buffer.length_ = 0; 1829 1830 if (isInline()) { 1831 inlineElem_ = nullptr; 1832 return; 1833 } 1834 1835 elems_ = reinterpret_cast<T*>(buffer.elems_); 1836 buffer.elems_ = nullptr; 1837 1838 #ifdef DEBUG 1839 for (size_t i = 0; i < length_; i++) { 1840 MOZ_ASSERT(elems_[i] == nullptr); 1841 } 1842 #endif 1843 } 1844 1845 template <typename T> 1846 void PreAllocateableGCArray<T>::trace(JSTracer* trc) { 1847 if (empty()) { 1848 return; 1849 } 1850 1851 if (isInline()) { 1852 TraceNullableRoot(trc, &inlineElem_, "PreAllocateableGCArray::inlineElem_"); 1853 return; 1854 } 1855 1856 for (size_t i = 0; i < length_; i++) { 1857 TraceNullableRoot(trc, &elems_[i], "PreAllocateableGCArray::elems_"); 1858 } 1859 } 1860 1861 template <typename T> 1862 PreAllocateableGCArray<T>::Preallocated::~Preallocated() { 1863 if (elems_) { 1864 js_free(elems_); 1865 elems_ = nullptr; 1866 } 1867 } 1868 1869 template <typename T> 1870 bool PreAllocateableGCArray<T>::Preallocated::allocate(size_t length) { 1871 MOZ_ASSERT(empty()); 1872 1873 length_ = length; 1874 1875 if (isInline()) { 1876 return true; 1877 } 1878 1879 elems_ = reinterpret_cast<uintptr_t*>(js_calloc(sizeof(uintptr_t) * length_)); 1880 if (!elems_) { 1881 return false; 1882 } 1883 1884 return true; 1885 } 1886 1887 template struct js::frontend::PreAllocateableGCArray<JSFunction*>; 1888 template struct js::frontend::PreAllocateableGCArray<js::Scope*>; 1889 1890 void CompilationAtomCache::trace(JSTracer* trc) { atoms_.trace(trc); } 1891 1892 void CompilationGCOutput::trace(JSTracer* trc) { 1893 TraceNullableRoot(trc, &script, "compilation-gc-output-script"); 1894 TraceNullableRoot(trc, &module, "compilation-gc-output-module"); 1895 TraceNullableRoot(trc, &sourceObject, "compilation-gc-output-source"); 1896 functions.trace(trc); 1897 scopes.trace(trc); 1898 } 1899 1900 RegExpObject* RegExpStencil::createRegExp( 1901 JSContext* cx, const CompilationAtomCache& atomCache) const { 1902 Rooted<JSAtom*> atom(cx, atomCache.getExistingAtomAt(cx, atom_)); 1903 return RegExpObject::createSyntaxChecked(cx, atom, flags(), TenuredObject); 1904 } 1905 1906 RegExpObject* RegExpStencil::createRegExpAndEnsureAtom( 1907 JSContext* cx, FrontendContext* fc, ParserAtomsTable& parserAtoms, 1908 CompilationAtomCache& atomCache) const { 1909 Rooted<JSAtom*> atom(cx, parserAtoms.toJSAtom(cx, fc, atom_, atomCache)); 1910 if (!atom) { 1911 return nullptr; 1912 } 1913 return RegExpObject::createSyntaxChecked(cx, atom, flags(), TenuredObject); 1914 } 1915 1916 AbstractScopePtr ScopeStencil::enclosing( 1917 CompilationState& compilationState) const { 1918 if (hasEnclosing()) { 1919 return AbstractScopePtr(compilationState, enclosing()); 1920 } 1921 1922 return AbstractScopePtr::compilationEnclosingScope(compilationState); 1923 } 1924 1925 Scope* ScopeStencil::enclosingExistingScope( 1926 const CompilationInput& input, const CompilationGCOutput& gcOutput) const { 1927 if (hasEnclosing()) { 1928 Scope* result = gcOutput.getScopeNoBaseIndex(enclosing()); 1929 MOZ_ASSERT(result, "Scope must already exist to use this method"); 1930 return result; 1931 } 1932 1933 // When creating a scope based on the input and a gc-output, we assume that 1934 // the scope stencil that we are looking at has not been merged into another 1935 // stencil, and thus that we still have the compilation input of the stencil. 1936 // 1937 // Otherwise, if this was in the case of an input generated from a Stencil 1938 // instead of live-gc values, we would not know its associated gcOutput as it 1939 // might not even have one yet. 1940 return input.enclosingScope.variant().as<Scope*>(); 1941 } 1942 1943 Scope* ScopeStencil::createScope(JSContext* cx, CompilationInput& input, 1944 CompilationGCOutput& gcOutput, 1945 BaseParserScopeData* baseScopeData) const { 1946 Rooted<Scope*> enclosingScope(cx, enclosingExistingScope(input, gcOutput)); 1947 return createScope(cx, input.atomCache, enclosingScope, baseScopeData); 1948 } 1949 1950 Scope* ScopeStencil::createScope(JSContext* cx, CompilationAtomCache& atomCache, 1951 Handle<Scope*> enclosingScope, 1952 BaseParserScopeData* baseScopeData) const { 1953 switch (kind()) { 1954 case ScopeKind::Function: { 1955 using ScopeType = FunctionScope; 1956 MOZ_ASSERT(matchScopeKind<ScopeType>(kind())); 1957 return createSpecificScope<ScopeType, CallObject>( 1958 cx, atomCache, enclosingScope, baseScopeData); 1959 } 1960 case ScopeKind::Lexical: 1961 case ScopeKind::SimpleCatch: 1962 case ScopeKind::Catch: 1963 case ScopeKind::NamedLambda: 1964 case ScopeKind::StrictNamedLambda: 1965 case ScopeKind::FunctionLexical: { 1966 using ScopeType = LexicalScope; 1967 MOZ_ASSERT(matchScopeKind<ScopeType>(kind())); 1968 return createSpecificScope<ScopeType, BlockLexicalEnvironmentObject>( 1969 cx, atomCache, enclosingScope, baseScopeData); 1970 } 1971 case ScopeKind::ClassBody: { 1972 using ScopeType = ClassBodyScope; 1973 MOZ_ASSERT(matchScopeKind<ScopeType>(kind())); 1974 return createSpecificScope<ScopeType, BlockLexicalEnvironmentObject>( 1975 cx, atomCache, enclosingScope, baseScopeData); 1976 } 1977 case ScopeKind::FunctionBodyVar: { 1978 using ScopeType = VarScope; 1979 MOZ_ASSERT(matchScopeKind<ScopeType>(kind())); 1980 return createSpecificScope<ScopeType, VarEnvironmentObject>( 1981 cx, atomCache, enclosingScope, baseScopeData); 1982 } 1983 case ScopeKind::Global: 1984 case ScopeKind::NonSyntactic: { 1985 using ScopeType = GlobalScope; 1986 MOZ_ASSERT(matchScopeKind<ScopeType>(kind())); 1987 return createSpecificScope<ScopeType, std::nullptr_t>( 1988 cx, atomCache, enclosingScope, baseScopeData); 1989 } 1990 case ScopeKind::Eval: 1991 case ScopeKind::StrictEval: { 1992 using ScopeType = EvalScope; 1993 MOZ_ASSERT(matchScopeKind<ScopeType>(kind())); 1994 return createSpecificScope<ScopeType, VarEnvironmentObject>( 1995 cx, atomCache, enclosingScope, baseScopeData); 1996 } 1997 case ScopeKind::Module: { 1998 using ScopeType = ModuleScope; 1999 MOZ_ASSERT(matchScopeKind<ScopeType>(kind())); 2000 return createSpecificScope<ScopeType, ModuleEnvironmentObject>( 2001 cx, atomCache, enclosingScope, baseScopeData); 2002 } 2003 case ScopeKind::With: { 2004 using ScopeType = WithScope; 2005 MOZ_ASSERT(matchScopeKind<ScopeType>(kind())); 2006 return createSpecificScope<ScopeType, std::nullptr_t>( 2007 cx, atomCache, enclosingScope, baseScopeData); 2008 } 2009 case ScopeKind::WasmFunction: 2010 case ScopeKind::WasmInstance: { 2011 // ScopeStencil does not support WASM 2012 break; 2013 } 2014 } 2015 MOZ_CRASH(); 2016 } 2017 2018 bool CompilationState::prepareSharedDataStorage(FrontendContext* fc) { 2019 size_t allScriptCount = scriptData.length(); 2020 size_t nonLazyScriptCount = nonLazyFunctionCount; 2021 if (!scriptData[0].isFunction()) { 2022 nonLazyScriptCount++; 2023 } 2024 return sharedData.prepareStorageFor(fc, nonLazyScriptCount, allScriptCount); 2025 } 2026 2027 static bool CreateLazyScript(JSContext* cx, 2028 const CompilationAtomCache& atomCache, 2029 const CompilationStencil& stencil, 2030 CompilationGCOutput& gcOutput, 2031 const ScriptStencil& script, 2032 const ScriptStencilExtra& scriptExtra, 2033 ScriptIndex scriptIndex, HandleFunction function) { 2034 Rooted<ScriptSourceObject*> sourceObject(cx, gcOutput.sourceObject); 2035 2036 size_t ngcthings = script.gcThingsLength; 2037 2038 Rooted<BaseScript*> lazy( 2039 cx, BaseScript::CreateRawLazy(cx, ngcthings, function, sourceObject, 2040 scriptExtra.extent, 2041 scriptExtra.immutableFlags)); 2042 if (!lazy) { 2043 return false; 2044 } 2045 2046 if (ngcthings) { 2047 if (!EmitScriptThingsVector(cx, atomCache, stencil, gcOutput, 2048 script.gcthings(stencil), 2049 lazy->gcthingsForInit())) { 2050 return false; 2051 } 2052 } 2053 2054 if (scriptExtra.useMemberInitializers()) { 2055 lazy->setMemberInitializers(scriptExtra.memberInitializers()); 2056 } 2057 2058 function->initScript(lazy); 2059 2060 return true; 2061 } 2062 2063 // Parser-generated functions with the same prototype will share the same shape. 2064 // By computing the correct values up front, we can save a lot of time in the 2065 // Object creation code. For simplicity, we focus only on plain synchronous 2066 // functions which are by far the most common. 2067 // 2068 // NOTE: Keep this in sync with `js::NewFunctionWithProto`. 2069 static JSFunction* CreateFunctionFast(JSContext* cx, 2070 CompilationAtomCache& atomCache, 2071 Handle<SharedShape*> shape, 2072 const ScriptStencil& script, 2073 const ScriptStencilExtra& scriptExtra) { 2074 MOZ_ASSERT( 2075 !scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsAsync)); 2076 MOZ_ASSERT(!scriptExtra.immutableFlags.hasFlag( 2077 ImmutableScriptFlagsEnum::IsGenerator)); 2078 MOZ_ASSERT(!script.functionFlags.isAsmJSNative()); 2079 2080 FunctionFlags flags = script.functionFlags; 2081 gc::AllocKind allocKind = flags.isExtended() 2082 ? gc::AllocKind::FUNCTION_EXTENDED 2083 : gc::AllocKind::FUNCTION; 2084 2085 JSFunction* fun = JSFunction::create(cx, allocKind, gc::Heap::Tenured, shape); 2086 if (!fun) { 2087 return nullptr; 2088 } 2089 2090 fun->setArgCount(scriptExtra.nargs); 2091 fun->setFlags(flags); 2092 2093 fun->initScript(nullptr); 2094 fun->initEnvironment(nullptr); 2095 2096 if (script.functionAtom) { 2097 JSAtom* atom = atomCache.getExistingAtomAt(cx, script.functionAtom); 2098 MOZ_ASSERT(atom); 2099 fun->initAtom(atom); 2100 } 2101 2102 #ifdef DEBUG 2103 fun->assertFunctionKindIntegrity(); 2104 #endif 2105 2106 return fun; 2107 } 2108 2109 static JSFunction* CreateFunction(JSContext* cx, 2110 CompilationAtomCache& atomCache, 2111 const CompilationStencil& stencil, 2112 const ScriptStencil& script, 2113 const ScriptStencilExtra& scriptExtra, 2114 ScriptIndex functionIndex) { 2115 GeneratorKind generatorKind = 2116 scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsGenerator) 2117 ? GeneratorKind::Generator 2118 : GeneratorKind::NotGenerator; 2119 FunctionAsyncKind asyncKind = 2120 scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsAsync) 2121 ? FunctionAsyncKind::AsyncFunction 2122 : FunctionAsyncKind::SyncFunction; 2123 2124 // Determine the new function's proto. This must be done for singleton 2125 // functions. 2126 RootedObject proto(cx); 2127 if (!GetFunctionPrototype(cx, generatorKind, asyncKind, &proto)) { 2128 return nullptr; 2129 } 2130 2131 gc::AllocKind allocKind = script.functionFlags.isExtended() 2132 ? gc::AllocKind::FUNCTION_EXTENDED 2133 : gc::AllocKind::FUNCTION; 2134 bool isAsmJS = script.functionFlags.isAsmJSNative(); 2135 2136 JSNative maybeNative = isAsmJS ? InstantiateAsmJS : nullptr; 2137 2138 Rooted<JSAtom*> displayAtom(cx); 2139 if (script.functionAtom) { 2140 displayAtom.set(atomCache.getExistingAtomAt(cx, script.functionAtom)); 2141 MOZ_ASSERT(displayAtom); 2142 } 2143 RootedFunction fun( 2144 cx, NewFunctionWithProto(cx, maybeNative, scriptExtra.nargs, 2145 script.functionFlags, nullptr, displayAtom, 2146 proto, allocKind, TenuredObject)); 2147 if (!fun) { 2148 return nullptr; 2149 } 2150 2151 if (isAsmJS) { 2152 RefPtr<const JS::WasmModule> asmJS = 2153 stencil.asmJS->moduleMap.lookup(functionIndex)->value(); 2154 2155 JSObject* moduleObj = asmJS->createObjectForAsmJS(cx); 2156 if (!moduleObj) { 2157 return nullptr; 2158 } 2159 2160 fun->setExtendedSlot(FunctionExtended::ASMJS_MODULE_SLOT, 2161 ObjectValue(*moduleObj)); 2162 } 2163 2164 return fun; 2165 } 2166 2167 static bool InstantiateAtoms(JSContext* cx, FrontendContext* fc, 2168 CompilationAtomCache& atomCache, 2169 const CompilationStencil& stencil) { 2170 return InstantiateMarkedAtoms(cx, fc, stencil.parserAtomData, atomCache); 2171 } 2172 2173 static bool InstantiateScriptSourceObject(JSContext* cx, 2174 const JS::InstantiateOptions& options, 2175 const CompilationStencil& stencil, 2176 CompilationGCOutput& gcOutput) { 2177 MOZ_ASSERT(stencil.source); 2178 2179 gcOutput.sourceObject = ScriptSourceObject::create(cx, stencil.source.get()); 2180 if (!gcOutput.sourceObject) { 2181 return false; 2182 } 2183 2184 Rooted<ScriptSourceObject*> sourceObject(cx, gcOutput.sourceObject); 2185 if (!ScriptSourceObject::initFromOptions(cx, sourceObject, options)) { 2186 return false; 2187 } 2188 2189 return true; 2190 } 2191 2192 // Instantiate ModuleObject. Further initialization is done after the associated 2193 // BaseScript is instantiated in InstantiateTopLevel. 2194 static bool InstantiateModuleObject(JSContext* cx, FrontendContext* fc, 2195 CompilationAtomCache& atomCache, 2196 const CompilationStencil& stencil, 2197 CompilationGCOutput& gcOutput) { 2198 MOZ_ASSERT(stencil.isModule()); 2199 2200 gcOutput.module = ModuleObject::create(cx); 2201 if (!gcOutput.module) { 2202 return false; 2203 } 2204 2205 Rooted<ModuleObject*> module(cx, gcOutput.module); 2206 return stencil.moduleMetadata->initModule(cx, fc, atomCache, module); 2207 } 2208 2209 // Instantiate JSFunctions for each FunctionBox. 2210 static bool InstantiateFunctions(JSContext* cx, FrontendContext* fc, 2211 CompilationAtomCache& atomCache, 2212 const CompilationStencil& stencil, 2213 CompilationGCOutput& gcOutput) { 2214 using ImmutableFlags = ImmutableScriptFlagsEnum; 2215 2216 MOZ_ASSERT(gcOutput.functions.length() == stencil.scriptData.size()); 2217 2218 // Most JSFunctions will be have the same Shape so we can compute it now to 2219 // allow fast object creation. Generators / Async will use the slow path 2220 // instead. 2221 Rooted<SharedShape*> functionShape( 2222 cx, GlobalObject::getFunctionShapeWithDefaultProto( 2223 cx, /* extended = */ false)); 2224 if (!functionShape) { 2225 return false; 2226 } 2227 2228 Rooted<SharedShape*> extendedShape( 2229 cx, GlobalObject::getFunctionShapeWithDefaultProto( 2230 cx, /* extended = */ true)); 2231 if (!extendedShape) { 2232 return false; 2233 } 2234 2235 for (auto item : 2236 CompilationStencil::functionScriptStencils(stencil, gcOutput)) { 2237 const auto& scriptStencil = item.script; 2238 const auto& scriptExtra = (*item.scriptExtra); 2239 auto index = item.index; 2240 2241 MOZ_ASSERT(!item.function); 2242 2243 // Plain functions can use a fast path. 2244 bool useFastPath = 2245 !scriptExtra.immutableFlags.hasFlag(ImmutableFlags::IsAsync) && 2246 !scriptExtra.immutableFlags.hasFlag(ImmutableFlags::IsGenerator) && 2247 !scriptStencil.functionFlags.isAsmJSNative(); 2248 2249 JSFunction* fun; 2250 if (useFastPath) { 2251 Handle<SharedShape*> shape = scriptStencil.functionFlags.isExtended() 2252 ? extendedShape 2253 : functionShape; 2254 fun = 2255 CreateFunctionFast(cx, atomCache, shape, scriptStencil, scriptExtra); 2256 } else { 2257 fun = CreateFunction(cx, atomCache, stencil, scriptStencil, scriptExtra, 2258 index); 2259 } 2260 2261 if (!fun) { 2262 return false; 2263 } 2264 2265 // Self-hosted functions may have a canonical name to use when instantiating 2266 // into other realms. 2267 if (scriptStencil.hasSelfHostedCanonicalName()) { 2268 JSAtom* canonicalName = atomCache.getExistingAtomAt( 2269 cx, scriptStencil.selfHostedCanonicalName()); 2270 fun->setAtom(canonicalName); 2271 } 2272 2273 gcOutput.getFunctionNoBaseIndex(index) = fun; 2274 } 2275 2276 return true; 2277 } 2278 2279 // Instantiate Scope for each ScopeStencil. 2280 // 2281 // This should be called after InstantiateFunctions, given FunctionScope needs 2282 // associated JSFunction pointer, and also should be called before 2283 // InstantiateScriptStencils, given JSScript needs Scope pointer in gc things. 2284 static bool InstantiateScopes(JSContext* cx, CompilationInput& input, 2285 const CompilationStencil& stencil, 2286 CompilationGCOutput& gcOutput) { 2287 // While allocating Scope object from ScopeStencil, Scope object for the 2288 // enclosing Scope should already be allocated. 2289 // 2290 // Enclosing scope of ScopeStencil can be either ScopeStencil or Scope* 2291 // pointer. 2292 // 2293 // If the enclosing scope is ScopeStencil, it's guaranteed to be earlier 2294 // element in stencil.scopeData, because enclosing_ field holds 2295 // index into it, and newly created ScopeStencil is pushed back to the array. 2296 // 2297 // If the enclosing scope is Scope*, it's CompilationInput.enclosingScope. 2298 2299 MOZ_ASSERT(stencil.scopeData.size() == stencil.scopeNames.size()); 2300 size_t scopeCount = stencil.scopeData.size(); 2301 for (size_t i = 0; i < scopeCount; i++) { 2302 Scope* scope = stencil.scopeData[i].createScope(cx, input, gcOutput, 2303 stencil.scopeNames[i]); 2304 if (!scope) { 2305 return false; 2306 } 2307 gcOutput.scopes[i] = scope; 2308 } 2309 2310 return true; 2311 } 2312 2313 // Instantiate js::BaseScripts from ScriptStencils for inner functions of the 2314 // compilation. Note that standalone functions and functions being delazified 2315 // are handled below with other top-levels. 2316 static bool InstantiateScriptStencils(JSContext* cx, 2317 CompilationAtomCache& atomCache, 2318 const CompilationStencil& stencil, 2319 CompilationGCOutput& gcOutput) { 2320 MOZ_ASSERT(stencil.isInitialStencil()); 2321 2322 Rooted<JSFunction*> fun(cx); 2323 for (auto item : 2324 CompilationStencil::functionScriptStencils(stencil, gcOutput)) { 2325 auto& scriptStencil = item.script; 2326 auto* scriptExtra = item.scriptExtra; 2327 fun = item.function; 2328 auto index = item.index; 2329 if (scriptStencil.hasSharedData()) { 2330 // If the function was not referenced by enclosing script's bytecode, we 2331 // do not generate a BaseScript for it. For example, `(function(){});`. 2332 // 2333 // `wasEmittedByEnclosingScript` is false also for standalone 2334 // functions. They are handled in InstantiateTopLevel. 2335 if (!scriptStencil.wasEmittedByEnclosingScript()) { 2336 continue; 2337 } 2338 2339 RootedScript script( 2340 cx, JSScript::fromStencil(cx, atomCache, stencil, gcOutput, index)); 2341 if (!script) { 2342 return false; 2343 } 2344 2345 if (scriptStencil.allowRelazify()) { 2346 MOZ_ASSERT(script->isRelazifiable()); 2347 script->setAllowRelazify(); 2348 } 2349 } else if (scriptStencil.functionFlags.isAsmJSNative()) { 2350 MOZ_ASSERT(fun->isAsmJSNative()); 2351 } else { 2352 MOZ_ASSERT(fun->isIncomplete()); 2353 if (!CreateLazyScript(cx, atomCache, stencil, gcOutput, scriptStencil, 2354 *scriptExtra, index, fun)) { 2355 return false; 2356 } 2357 } 2358 } 2359 2360 return true; 2361 } 2362 2363 // Instantiate the Stencil for the top-level script of the compilation. This 2364 // includes standalone functions and functions being delazified. 2365 static bool InstantiateTopLevel(JSContext* cx, CompilationInput& input, 2366 const CompilationStencil& stencil, 2367 CompilationGCOutput& gcOutput) { 2368 const ScriptStencil& scriptStencil = 2369 stencil.scriptData[CompilationStencil::TopLevelIndex]; 2370 2371 // Top-level asm.js does not generate a JSScript. 2372 if (scriptStencil.functionFlags.isAsmJSNative()) { 2373 return true; 2374 } 2375 2376 MOZ_ASSERT(scriptStencil.hasSharedData()); 2377 MOZ_ASSERT(stencil.sharedData.get(CompilationStencil::TopLevelIndex)); 2378 2379 if (!stencil.isInitialStencil()) { 2380 MOZ_ASSERT(input.lazyOuterBaseScript()); 2381 RootedScript script(cx, 2382 JSScript::CastFromLazy(input.lazyOuterBaseScript())); 2383 if (!JSScript::fullyInitFromStencil(cx, input.atomCache, stencil, gcOutput, 2384 script, 2385 CompilationStencil::TopLevelIndex)) { 2386 return false; 2387 } 2388 2389 if (scriptStencil.allowRelazify()) { 2390 MOZ_ASSERT(script->isRelazifiable()); 2391 script->setAllowRelazify(); 2392 } 2393 2394 gcOutput.script = script; 2395 return true; 2396 } 2397 2398 gcOutput.script = 2399 JSScript::fromStencil(cx, input.atomCache, stencil, gcOutput, 2400 CompilationStencil::TopLevelIndex); 2401 if (!gcOutput.script) { 2402 return false; 2403 } 2404 2405 if (scriptStencil.allowRelazify()) { 2406 MOZ_ASSERT(gcOutput.script->isRelazifiable()); 2407 gcOutput.script->setAllowRelazify(); 2408 } 2409 2410 const ScriptStencilExtra& scriptExtra = 2411 stencil.scriptExtra[CompilationStencil::TopLevelIndex]; 2412 2413 // Finish initializing the ModuleObject if needed. 2414 if (scriptExtra.isModule()) { 2415 RootedScript script(cx, gcOutput.script); 2416 Rooted<ModuleObject*> module(cx, gcOutput.module); 2417 2418 script->outermostScope()->as<ModuleScope>().initModule(module); 2419 2420 module->initScriptSlots(script); 2421 2422 if (!ModuleObject::createEnvironment(cx, module)) { 2423 return false; 2424 } 2425 2426 if (!ModuleObject::Freeze(cx, module)) { 2427 return false; 2428 } 2429 } 2430 2431 return true; 2432 } 2433 2434 // When a function is first referenced by enclosing script's bytecode, we need 2435 // to update it with information determined by the BytecodeEmitter. This applies 2436 // to both initial and delazification parses. The functions being update may or 2437 // may not have bytecode at this point. 2438 static void UpdateEmittedInnerFunctions(JSContext* cx, 2439 CompilationAtomCache& atomCache, 2440 const CompilationStencil& stencil, 2441 CompilationGCOutput& gcOutput) { 2442 for (auto item : 2443 CompilationStencil::functionScriptStencils(stencil, gcOutput)) { 2444 auto& scriptStencil = item.script; 2445 auto& fun = item.function; 2446 if (!scriptStencil.wasEmittedByEnclosingScript()) { 2447 continue; 2448 } 2449 2450 if (scriptStencil.functionFlags.isAsmJSNative() || 2451 fun->baseScript()->hasBytecode()) { 2452 // Non-lazy inner functions don't use the enclosingScope_ field. 2453 MOZ_ASSERT(!scriptStencil.hasLazyFunctionEnclosingScopeIndex()); 2454 } else { 2455 // Apply updates from FunctionEmitter::emitLazy(). 2456 BaseScript* script = fun->baseScript(); 2457 2458 ScopeIndex index = scriptStencil.lazyFunctionEnclosingScopeIndex(); 2459 Scope* scope = gcOutput.getScopeNoBaseIndex(index); 2460 script->setEnclosingScope(scope); 2461 2462 // Inferred and Guessed names are computed by BytecodeEmitter and so may 2463 // need to be applied to existing JSFunctions during delazification. 2464 if (fun->fullDisplayAtom() == nullptr) { 2465 JSAtom* funcAtom = nullptr; 2466 if (scriptStencil.functionFlags.hasInferredName() || 2467 scriptStencil.functionFlags.hasGuessedAtom()) { 2468 funcAtom = 2469 atomCache.getExistingAtomAt(cx, scriptStencil.functionAtom); 2470 MOZ_ASSERT(funcAtom); 2471 } 2472 if (scriptStencil.functionFlags.hasInferredName()) { 2473 fun->setInferredName(funcAtom); 2474 } 2475 if (scriptStencil.functionFlags.hasGuessedAtom()) { 2476 fun->setGuessedAtom(funcAtom); 2477 } 2478 } 2479 } 2480 } 2481 } 2482 2483 // During initial parse we must link lazy-functions-inside-lazy-functions to 2484 // their enclosing script. 2485 static void LinkEnclosingLazyScript(const CompilationStencil& stencil, 2486 CompilationGCOutput& gcOutput) { 2487 for (auto item : 2488 CompilationStencil::functionScriptStencils(stencil, gcOutput)) { 2489 auto& scriptStencil = item.script; 2490 auto& fun = item.function; 2491 if (!scriptStencil.functionFlags.hasBaseScript()) { 2492 continue; 2493 } 2494 2495 if (!fun->baseScript()) { 2496 continue; 2497 } 2498 2499 if (fun->baseScript()->hasBytecode()) { 2500 continue; 2501 } 2502 2503 BaseScript* script = fun->baseScript(); 2504 MOZ_ASSERT(!script->hasBytecode()); 2505 2506 for (auto inner : script->gcthings()) { 2507 if (!inner.is<JSObject>()) { 2508 continue; 2509 } 2510 JSFunction* innerFun = &inner.as<JSObject>().as<JSFunction>(); 2511 2512 MOZ_ASSERT(innerFun->hasBaseScript(), 2513 "inner function should have base script"); 2514 if (!innerFun->hasBaseScript()) { 2515 continue; 2516 } 2517 2518 // Check for the case that the inner function has the base script flag, 2519 // but still doesn't have the actual base script pointer. 2520 // `baseScript` method asserts the pointer itself, so no extra MOZ_ASSERT 2521 // here. 2522 if (!innerFun->baseScript()) { 2523 continue; 2524 } 2525 2526 innerFun->setEnclosingLazyScript(script); 2527 } 2528 } 2529 } 2530 2531 #ifdef DEBUG 2532 // Some fields aren't used in delazification, given the target functions and 2533 // scripts are already instantiated, but they still should match. 2534 static void AssertDelazificationFieldsMatch(const CompilationStencil& stencil, 2535 CompilationGCOutput& gcOutput) { 2536 for (auto item : 2537 CompilationStencil::functionScriptStencils(stencil, gcOutput)) { 2538 auto& scriptStencil = item.script; 2539 auto* scriptExtra = item.scriptExtra; 2540 auto& fun = item.function; 2541 2542 MOZ_ASSERT(scriptExtra == nullptr); 2543 2544 // Names are updated by UpdateInnerFunctions. 2545 constexpr uint16_t HAS_INFERRED_NAME = 2546 uint16_t(FunctionFlags::Flags::HAS_INFERRED_NAME); 2547 constexpr uint16_t HAS_GUESSED_ATOM = 2548 uint16_t(FunctionFlags::Flags::HAS_GUESSED_ATOM); 2549 constexpr uint16_t MUTABLE_FLAGS = 2550 uint16_t(FunctionFlags::Flags::MUTABLE_FLAGS); 2551 constexpr uint16_t acceptableDifferenceForFunction = 2552 HAS_INFERRED_NAME | HAS_GUESSED_ATOM | MUTABLE_FLAGS; 2553 2554 MOZ_ASSERT((fun->flags().toRaw() | acceptableDifferenceForFunction) == 2555 (scriptStencil.functionFlags.toRaw() | 2556 acceptableDifferenceForFunction)); 2557 2558 // Delazification shouldn't delazify inner scripts. 2559 MOZ_ASSERT_IF(item.index == CompilationStencil::TopLevelIndex, 2560 scriptStencil.hasSharedData()); 2561 MOZ_ASSERT_IF(item.index > CompilationStencil::TopLevelIndex, 2562 !scriptStencil.hasSharedData()); 2563 } 2564 } 2565 #endif // DEBUG 2566 2567 // When delazifying, use the existing JSFunctions. The initial and delazifying 2568 // parse are required to generate the same sequence of functions for lazy 2569 // parsing to work at all. 2570 static void FunctionsFromExistingLazy(CompilationInput& input, 2571 CompilationGCOutput& gcOutput) { 2572 MOZ_ASSERT(!gcOutput.functions[0]); 2573 2574 size_t instantiatedFunIndex = 0; 2575 gcOutput.functions[instantiatedFunIndex++] = input.function(); 2576 2577 for (JS::GCCellPtr elem : input.lazyOuterBaseScript()->gcthings()) { 2578 if (!elem.is<JSObject>()) { 2579 continue; 2580 } 2581 JSFunction* fun = &elem.as<JSObject>().as<JSFunction>(); 2582 gcOutput.functions[instantiatedFunIndex++] = fun; 2583 } 2584 } 2585 2586 void CompilationStencil::borrowFromExtensibleCompilationStencil( 2587 ExtensibleCompilationStencil& extensibleStencil) { 2588 canLazilyParse = extensibleStencil.canLazilyParse; 2589 functionKey = extensibleStencil.functionKey; 2590 2591 // Borrow the vector content as span. 2592 scriptData = extensibleStencil.scriptData; 2593 scriptExtra = extensibleStencil.scriptExtra; 2594 2595 gcThingData = extensibleStencil.gcThingData; 2596 2597 scopeData = extensibleStencil.scopeData; 2598 scopeNames = extensibleStencil.scopeNames; 2599 2600 regExpData = extensibleStencil.regExpData; 2601 bigIntData = extensibleStencil.bigIntData; 2602 objLiteralData = extensibleStencil.objLiteralData; 2603 2604 // Borrow the parser atoms as span. 2605 parserAtomData = extensibleStencil.parserAtoms.entries_; 2606 2607 // Borrow container. 2608 sharedData.setBorrow(&extensibleStencil.sharedData); 2609 2610 // Share ref-counted data. 2611 source = extensibleStencil.source; 2612 asmJS = extensibleStencil.asmJS; 2613 moduleMetadata = extensibleStencil.moduleMetadata; 2614 } 2615 2616 #ifdef DEBUG 2617 void CompilationStencil::assertBorrowingFromExtensibleCompilationStencil( 2618 const ExtensibleCompilationStencil& extensibleStencil) const { 2619 MOZ_ASSERT(canLazilyParse == extensibleStencil.canLazilyParse); 2620 MOZ_ASSERT(functionKey == extensibleStencil.functionKey); 2621 2622 AssertBorrowingSpan(scriptData, extensibleStencil.scriptData); 2623 AssertBorrowingSpan(scriptExtra, extensibleStencil.scriptExtra); 2624 2625 AssertBorrowingSpan(gcThingData, extensibleStencil.gcThingData); 2626 2627 AssertBorrowingSpan(scopeData, extensibleStencil.scopeData); 2628 AssertBorrowingSpan(scopeNames, extensibleStencil.scopeNames); 2629 2630 AssertBorrowingSpan(regExpData, extensibleStencil.regExpData); 2631 AssertBorrowingSpan(bigIntData, extensibleStencil.bigIntData); 2632 AssertBorrowingSpan(objLiteralData, extensibleStencil.objLiteralData); 2633 2634 AssertBorrowingSpan(parserAtomData, extensibleStencil.parserAtoms.entries_); 2635 2636 MOZ_ASSERT(sharedData.isBorrow()); 2637 MOZ_ASSERT(sharedData.asBorrow() == &extensibleStencil.sharedData); 2638 2639 MOZ_ASSERT(source == extensibleStencil.source); 2640 MOZ_ASSERT(asmJS == extensibleStencil.asmJS); 2641 MOZ_ASSERT(moduleMetadata == extensibleStencil.moduleMetadata); 2642 } 2643 #endif 2644 2645 CompilationStencil::CompilationStencil( 2646 UniquePtr<ExtensibleCompilationStencil>&& extensibleStencil) 2647 : alloc(LifoAllocChunkSize, js::BackgroundMallocArena) { 2648 ownedBorrowStencil = std::move(extensibleStencil); 2649 2650 storageType = StorageType::OwnedExtensible; 2651 2652 borrowFromExtensibleCompilationStencil(*ownedBorrowStencil); 2653 2654 #ifdef DEBUG 2655 assertNoExternalDependency(); 2656 #endif 2657 } 2658 2659 // Instantiate JitScripts and eagerly baseline compile any potential 2660 // candidate functions. 2661 // 2662 // Return value indicates whether a failure occured. (i.e. allocation failure.) 2663 // There is no current indication of whether a function was actually dispatched 2664 // for eager baseline compilation. 2665 static bool MaybeDoEagerBaselineCompilations(JSContext* cx, 2666 const CompilationStencil& stencil, 2667 CompilationGCOutput& gcOutput, 2668 bool doAggressive) { 2669 if (!jit::IsBaselineInterpreterEnabled()) { 2670 return true; 2671 } 2672 2673 if (!cx->zone()->ensureJitZoneExists(cx)) { 2674 return false; 2675 } 2676 2677 jit::JitHintsMap* jitHints = nullptr; 2678 if (!doAggressive) { 2679 if (jit::JitOptions.disableJitHints || 2680 !cx->runtime()->jitRuntime()->hasJitHintsMap()) { 2681 return true; 2682 } 2683 jitHints = cx->runtime()->jitRuntime()->getJitHintsMap(); 2684 } 2685 2686 jit::AutoKeepJitScripts keepJitScript(cx); 2687 RootedScript script(cx); 2688 Rooted<JSFunction*> fn(cx); 2689 jit::BaselineCompileQueue& queue = cx->realm()->baselineCompileQueue(); 2690 2691 for (auto item : 2692 CompilationStencil::functionScriptStencils(stencil, gcOutput)) { 2693 fn = item.function; 2694 if (!fn->hasBytecode()) { 2695 continue; 2696 } 2697 2698 script = fn->nonLazyScript(); 2699 2700 // Only eagerly baseline compile functions with hints unless aggressive 2701 // strategy is set. 2702 if (!doAggressive) { 2703 if (!jitHints->mightHaveEagerBaselineHint(script)) { 2704 continue; 2705 } 2706 } 2707 2708 if (script->baselineDisabled()) { 2709 continue; 2710 } 2711 2712 if (!jit::CanBaselineInterpretScript(script)) { 2713 continue; 2714 } 2715 2716 if (!jit::BaselineCompileTask::OffThreadBaselineCompilationAvailable( 2717 cx, script, /* isEager = */ true)) { 2718 continue; 2719 } 2720 2721 // Add script to the baseline compile batch queue and dispatch if full. 2722 if (queue.numQueued() >= jit::JitOptions.baselineQueueCapacity) { 2723 if (!jit::DispatchOffThreadBaselineBatchEager(cx)) { 2724 return false; 2725 } 2726 TRACE_FOR_TEST_DOM(cx, "omt_eager_baseline_dispatch"); 2727 } 2728 2729 // Add script to queue 2730 if (!queue.enqueue(script)) { 2731 return false; 2732 } 2733 TRACE_FOR_TEST_DOM(cx, "omt_eager_baseline_function", script); 2734 } 2735 2736 // Dispatch any remaining scripts in the queue 2737 if (queue.numQueued() > 0) { 2738 if (!jit::DispatchOffThreadBaselineBatchEager(cx)) { 2739 return false; 2740 } 2741 TRACE_FOR_TEST_DOM(cx, "omt_eager_baseline_dispatch"); 2742 } 2743 2744 return true; 2745 } 2746 2747 /* static */ 2748 bool CompilationStencil::instantiateStencils(JSContext* cx, 2749 CompilationInput& input, 2750 const CompilationStencil& stencil, 2751 CompilationGCOutput& gcOutput) { 2752 AutoReportFrontendContext fc(cx); 2753 if (!prepareForInstantiate(&fc, input.atomCache, stencil, gcOutput)) { 2754 return false; 2755 } 2756 2757 if (!instantiateStencilAfterPreparation(cx, input, stencil, gcOutput)) { 2758 return false; 2759 } 2760 2761 if (input.options.eagerBaselineStrategy() != JS::EagerBaselineOption::None) { 2762 MOZ_ASSERT(!input.isDelazifying(), 2763 "No current support for eager baseline during delazifications."); 2764 2765 bool doAggressive = input.options.eagerBaselineStrategy() == 2766 JS::EagerBaselineOption::Aggressive; 2767 if (!MaybeDoEagerBaselineCompilations(cx, stencil, gcOutput, 2768 doAggressive)) { 2769 return false; 2770 } 2771 } 2772 2773 return true; 2774 } 2775 2776 /* static */ 2777 bool CompilationStencil::instantiateStencilAfterPreparation( 2778 JSContext* cx, CompilationInput& input, const CompilationStencil& stencil, 2779 CompilationGCOutput& gcOutput) { 2780 // Distinguish between the initial (possibly lazy) compile and any subsequent 2781 // delazification compiles. Delazification will update existing GC things. 2782 bool isInitialParse = stencil.isInitialStencil(); 2783 MOZ_ASSERT(stencil.isInitialStencil() == input.isInitialStencil()); 2784 2785 // Assert the consistency between the compile option and the target global. 2786 MOZ_ASSERT_IF(cx->realm()->behaviors().discardSource(), 2787 !stencil.canLazilyParse); 2788 2789 CompilationAtomCache& atomCache = input.atomCache; 2790 const JS::InstantiateOptions options(input.options); 2791 2792 // Phase 1: Instantiate JSAtom/JSStrings. 2793 AutoReportFrontendContext fc(cx); 2794 if (!InstantiateAtoms(cx, &fc, atomCache, stencil)) { 2795 return false; 2796 } 2797 2798 // Phase 2: Instantiate ScriptSourceObject, ModuleObject, JSFunctions. 2799 if (isInitialParse) { 2800 if (!InstantiateScriptSourceObject(cx, options, stencil, gcOutput)) { 2801 return false; 2802 } 2803 2804 if (stencil.moduleMetadata) { 2805 // The enclosing script of a module is always the global scope. Fetch the 2806 // scope of the current global and update input data. 2807 MOZ_ASSERT(input.enclosingScope.isNull()); 2808 input.enclosingScope = InputScope(&cx->global()->emptyGlobalScope()); 2809 MOZ_ASSERT(input.enclosingScope.environmentChainLength() == 2810 ModuleScope::EnclosingEnvironmentChainLength); 2811 2812 if (!InstantiateModuleObject(cx, &fc, atomCache, stencil, gcOutput)) { 2813 return false; 2814 } 2815 } 2816 2817 if (!InstantiateFunctions(cx, &fc, atomCache, stencil, gcOutput)) { 2818 return false; 2819 } 2820 } else { 2821 MOZ_ASSERT( 2822 stencil.scriptData[CompilationStencil::TopLevelIndex].isFunction()); 2823 2824 // FunctionKey is used when caching to map a delazification stencil to a 2825 // specific lazy script. It is not used by instantiation, but we should 2826 // ensure it is correctly defined. 2827 MOZ_ASSERT(stencil.functionKey == input.extent().toFunctionKey()); 2828 2829 FunctionsFromExistingLazy(input, gcOutput); 2830 MOZ_ASSERT(gcOutput.functions.length() == stencil.scriptData.size()); 2831 2832 #ifdef DEBUG 2833 AssertDelazificationFieldsMatch(stencil, gcOutput); 2834 #endif 2835 } 2836 2837 // Phase 3: Instantiate js::Scopes. 2838 if (!InstantiateScopes(cx, input, stencil, gcOutput)) { 2839 return false; 2840 } 2841 2842 // Phase 4: Instantiate (inner) BaseScripts. 2843 if (isInitialParse) { 2844 if (!InstantiateScriptStencils(cx, atomCache, stencil, gcOutput)) { 2845 return false; 2846 } 2847 } 2848 2849 // Phase 5: Finish top-level handling 2850 if (!InstantiateTopLevel(cx, input, stencil, gcOutput)) { 2851 return false; 2852 } 2853 2854 // !! Must be infallible from here forward !! 2855 2856 // Phase 6: Update lazy scripts. 2857 if (stencil.canLazilyParse) { 2858 UpdateEmittedInnerFunctions(cx, atomCache, stencil, gcOutput); 2859 2860 if (isInitialParse) { 2861 LinkEnclosingLazyScript(stencil, gcOutput); 2862 } 2863 } 2864 2865 // Trigger the use counter for asm.js. This should fire even if asm.js 2866 // optimizations are disabled, see the comment in FunctionBox::setUseAsm() 2867 // for how we do that. 2868 if (stencil.hasAsmJS()) { 2869 cx->runtime()->setUseCounter(cx->global(), JSUseCounter::USE_ASM); 2870 } 2871 2872 return true; 2873 } 2874 2875 // The top-level self-hosted script is created and executed in each realm that 2876 // needs it. While the stencil has a gcthings list for the various top-level 2877 // functions, we use special machinery to create them on demand. So instead we 2878 // use a placeholder JSFunction that should never be called. 2879 static bool SelfHostedDummyFunction(JSContext* cx, unsigned argc, 2880 JS::Value* vp) { 2881 MOZ_CRASH("Self-hosting top-level should not use functions directly"); 2882 } 2883 2884 bool CompilationStencil::instantiateSelfHostedAtoms( 2885 JSContext* cx, AtomSet& atomSet, CompilationAtomCache& atomCache) const { 2886 MOZ_ASSERT(isInitialStencil()); 2887 2888 // We must instantiate atoms during startup so they can be made permanent 2889 // across multiple runtimes. 2890 AutoReportFrontendContext fc(cx); 2891 return InstantiateMarkedAtomsAsPermanent(cx, &fc, atomSet, parserAtomData, 2892 atomCache); 2893 } 2894 2895 JSScript* CompilationStencil::instantiateSelfHostedTopLevelForRealm( 2896 JSContext* cx, CompilationInput& input) { 2897 MOZ_ASSERT(isInitialStencil()); 2898 2899 Rooted<CompilationGCOutput> gcOutput(cx); 2900 2901 gcOutput.get().sourceObject = SelfHostingScriptSourceObject(cx); 2902 if (!gcOutput.get().sourceObject) { 2903 return nullptr; 2904 } 2905 2906 // The top-level script has ScriptIndex references in its gcthings list, but 2907 // we do not want to instantiate those functions here since they are instead 2908 // created on demand from the stencil. Create a dummy function and populate 2909 // the functions array of the CompilationGCOutput with references to it. 2910 RootedFunction dummy( 2911 cx, NewNativeFunction(cx, SelfHostedDummyFunction, 0, nullptr)); 2912 if (!dummy) { 2913 return nullptr; 2914 } 2915 2916 if (!gcOutput.get().functions.allocateWith(dummy, scriptData.size())) { 2917 ReportOutOfMemory(cx); 2918 return nullptr; 2919 } 2920 2921 if (!InstantiateTopLevel(cx, input, *this, gcOutput.get())) { 2922 return nullptr; 2923 } 2924 2925 return gcOutput.get().script; 2926 } 2927 2928 JSFunction* CompilationStencil::instantiateSelfHostedLazyFunction( 2929 JSContext* cx, CompilationAtomCache& atomCache, ScriptIndex index, 2930 Handle<JSAtom*> name) { 2931 MOZ_ASSERT(cx->zone()->suppressAllocationMetadataBuilder); 2932 2933 GeneratorKind generatorKind = scriptExtra[index].immutableFlags.hasFlag( 2934 ImmutableScriptFlagsEnum::IsGenerator) 2935 ? GeneratorKind::Generator 2936 : GeneratorKind::NotGenerator; 2937 FunctionAsyncKind asyncKind = scriptExtra[index].immutableFlags.hasFlag( 2938 ImmutableScriptFlagsEnum::IsAsync) 2939 ? FunctionAsyncKind::AsyncFunction 2940 : FunctionAsyncKind::SyncFunction; 2941 2942 Rooted<JSAtom*> funName(cx); 2943 if (scriptData[index].hasSelfHostedCanonicalName()) { 2944 // SetCanonicalName was used to override the name. 2945 funName = atomCache.getExistingAtomAt( 2946 cx, scriptData[index].selfHostedCanonicalName()); 2947 } else if (name) { 2948 // Our caller has a name it wants to use. 2949 funName = name; 2950 } else { 2951 MOZ_ASSERT(scriptData[index].functionAtom); 2952 funName = atomCache.getExistingAtomAt(cx, scriptData[index].functionAtom); 2953 } 2954 2955 RootedObject proto(cx); 2956 if (!GetFunctionPrototype(cx, generatorKind, asyncKind, &proto)) { 2957 return nullptr; 2958 } 2959 2960 RootedObject env(cx, &cx->global()->lexicalEnvironment()); 2961 2962 JSFunction* fun = NewFunctionWithProto( 2963 cx, nullptr, scriptExtra[index].nargs, scriptData[index].functionFlags, 2964 env, funName, proto, gc::AllocKind::FUNCTION_EXTENDED, TenuredObject); 2965 if (!fun) { 2966 return nullptr; 2967 } 2968 2969 fun->initSelfHostedLazyScript(&cx->runtime()->selfHostedLazyScript.ref()); 2970 2971 JSAtom* selfHostedName = 2972 atomCache.getExistingAtomAt(cx, scriptData[index].functionAtom); 2973 SetClonedSelfHostedFunctionName(fun, selfHostedName->asPropertyName()); 2974 2975 return fun; 2976 } 2977 2978 bool CompilationStencil::delazifySelfHostedFunction( 2979 JSContext* cx, CompilationAtomCache& atomCache, ScriptIndexRange range, 2980 Handle<JSAtom*> name, HandleFunction fun) { 2981 // Determine the equivalent ScopeIndex range by looking at the outermost scope 2982 // of the scripts defining the range. Take special care if this is the last 2983 // script in the list. 2984 auto getOutermostScope = [this](ScriptIndex scriptIndex) -> ScopeIndex { 2985 MOZ_ASSERT(scriptData[scriptIndex].hasSharedData()); 2986 auto gcthings = scriptData[scriptIndex].gcthings(*this); 2987 return gcthings[GCThingIndex::outermostScopeIndex()].toScope(); 2988 }; 2989 ScopeIndex scopeIndex = getOutermostScope(range.start); 2990 ScopeIndex scopeLimit = (range.limit < scriptData.size()) 2991 ? getOutermostScope(range.limit) 2992 : ScopeIndex(scopeData.size()); 2993 2994 // Prepare to instantiate by allocating the output arrays. We also set a base 2995 // index to avoid allocations in most cases. 2996 AutoReportFrontendContext fc(cx); 2997 Rooted<CompilationGCOutput> gcOutput(cx); 2998 if (!gcOutput.get().ensureAllocatedWithBaseIndex( 2999 &fc, range.start, range.limit, scopeIndex, scopeLimit)) { 3000 return false; 3001 } 3002 3003 // Phase 1: Instantiate JSAtoms. 3004 // NOTE: The self-hosted atoms are all "permanent" and the 3005 // CompilationAtomCache is already stored on the JSRuntime. 3006 3007 // Phase 2: Instantiate ScriptSourceObject, ModuleObject, JSFunctions. 3008 3009 // Get the corresponding ScriptSourceObject to use in current realm. 3010 gcOutput.get().sourceObject = SelfHostingScriptSourceObject(cx); 3011 if (!gcOutput.get().sourceObject) { 3012 return false; 3013 } 3014 3015 size_t instantiatedFunIndex = 0; 3016 3017 // Delazification target function. 3018 gcOutput.get().functions[instantiatedFunIndex++] = fun; 3019 3020 // Allocate inner functions. Self-hosted functions do not allocate these with 3021 // the initial function. 3022 for (size_t i = range.start + 1; i < range.limit; i++) { 3023 JSFunction* innerFun = CreateFunction(cx, atomCache, *this, scriptData[i], 3024 scriptExtra[i], ScriptIndex(i)); 3025 if (!innerFun) { 3026 return false; 3027 } 3028 gcOutput.get().functions[instantiatedFunIndex++] = innerFun; 3029 } 3030 3031 // Phase 3: Instantiate js::Scopes. 3032 // NOTE: When the enclosing scope is not a stencil, directly use the 3033 // `emptyGlobalScope` instead of reading from CompilationInput. This is 3034 // a special case for self-hosted delazification that allows us to reuse 3035 // the CompilationInput between different realms. 3036 size_t instantiatedScopeIndex = 0; 3037 for (size_t i = scopeIndex; i < scopeLimit; i++) { 3038 ScopeStencil& data = scopeData[i]; 3039 Rooted<Scope*> enclosingScope( 3040 cx, data.hasEnclosing() ? gcOutput.get().getScope(data.enclosing()) 3041 : &cx->global()->emptyGlobalScope()); 3042 3043 js::Scope* scope = 3044 data.createScope(cx, atomCache, enclosingScope, scopeNames[i]); 3045 if (!scope) { 3046 return false; 3047 } 3048 gcOutput.get().scopes[instantiatedScopeIndex++] = scope; 3049 } 3050 3051 // Phase 4: Instantiate (inner) BaseScripts. 3052 ScriptIndex innerStart(range.start + 1); 3053 for (size_t i = innerStart; i < range.limit; i++) { 3054 if (!JSScript::fromStencil(cx, atomCache, *this, gcOutput.get(), 3055 ScriptIndex(i))) { 3056 return false; 3057 } 3058 } 3059 3060 // Phase 5: Finish top-level handling 3061 // NOTE: We do not have a `CompilationInput` handy here, so avoid using the 3062 // `InstantiateTopLevel` helper and directly create the JSScript. Our 3063 // caller also handles the `AllowRelazify` flag for us since self-hosted 3064 // delazification is a special case. 3065 Rooted<JSScript*> script( 3066 cx, 3067 JSScript::fromStencil(cx, atomCache, *this, gcOutput.get(), range.start)); 3068 if (!script) { 3069 return false; 3070 } 3071 3072 if (JS::Prefs::experimental_self_hosted_cache()) { 3073 Rooted<JSRuntime::JitCacheKey> jitCacheKey(cx, name, script->isDebuggee()); 3074 3075 // We eagerly baseline-compile self-hosted functions, and cache their 3076 // JitCode for reuse across the runtime. If the cache already contains an 3077 // entry for this function, update the JitScript. If not, compile it now and 3078 // store it in the cache. 3079 UniqueChars nameStr; 3080 if (JS_SHOULD_LOG(selfHosted, Debug)) { 3081 nameStr = AtomToPrintableString(cx, name); 3082 } 3083 auto& jitCache = cx->runtime()->selfHostJitCache.ref(); 3084 auto v = jitCache.readonlyThreadsafeLookup(jitCacheKey); 3085 if (v && v->value()->method()) { 3086 JS_LOG(selfHosted, Debug, 3087 "self_hosted_cache: reusing JIT code for script '%s'", 3088 nameStr.get()); 3089 3090 if (!cx->zone()->ensureJitZoneExists(cx)) { 3091 return false; 3092 } 3093 jit::AutoKeepJitScripts keepJitScript(cx); 3094 if (!script->ensureHasJitScript(cx, keepJitScript)) { 3095 return false; 3096 } 3097 MOZ_ASSERT(!script->hasBaselineScript()); 3098 3099 // JSScript destroys its BaselineScript on finalize, so we need another 3100 // copy here (for now) 3101 jit::BaselineScript* baselineScript = 3102 jit::BaselineScript::Copy(cx, v->value()); 3103 if (!baselineScript) { 3104 return false; 3105 } 3106 mozilla::DebugOnly<bool> instrumentationEnabled = 3107 cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled( 3108 cx->runtime()); 3109 MOZ_ASSERT(instrumentationEnabled == 3110 baselineScript->isProfilerInstrumentationOn()); 3111 script->jitScript()->setBaselineScript(script, baselineScript); 3112 } else if (jit::IsBaselineJitEnabled(cx) && script->canBaselineCompile() && 3113 !script->hasBaselineScript() && 3114 jit::CanBaselineInterpretScript(script)) { 3115 JS_LOG(selfHosted, Debug, 3116 "self_hosted_cache: new JIT code entry for script '%s'", 3117 nameStr.get()); 3118 3119 if (!cx->zone()->ensureJitZoneExists(cx)) { 3120 return false; 3121 } 3122 3123 jit::AutoKeepJitScripts keep(cx); 3124 if (!script->ensureHasJitScript(cx, keep)) { 3125 return false; 3126 } 3127 3128 jit::BaselineOptions options( 3129 {jit::BaselineOption::ForceMainThreadCompilation}); 3130 jit::MethodStatus result = 3131 jit::BaselineCompile(cx, script.get(), options); 3132 if (result != jit::Method_Compiled) { 3133 return false; 3134 } 3135 MOZ_ASSERT(script->hasBaselineScript()); 3136 3137 jit::BaselineScript* baselineScript = 3138 jit::BaselineScript::Copy(cx, script->baselineScript()); 3139 if (!baselineScript) { 3140 return false; 3141 } 3142 if (!jitCache.put(jitCacheKey, baselineScript)) { 3143 return false; 3144 } 3145 } else { 3146 JS_LOG(selfHosted, Debug, 3147 "self_hosted_cache: script '%s' is not eligible for Baseline " 3148 "compilation", 3149 nameStr.get()); 3150 } 3151 } 3152 3153 // Phase 6: Update lazy scripts. 3154 // NOTE: Self-hosting is always fully parsed so there is nothing to do here. 3155 3156 return true; 3157 } 3158 3159 /* static */ 3160 bool CompilationStencil::prepareForInstantiate( 3161 FrontendContext* fc, CompilationAtomCache& atomCache, 3162 const CompilationStencil& stencil, CompilationGCOutput& gcOutput) { 3163 // Allocate the `gcOutput` arrays. 3164 if (!gcOutput.ensureAllocated(fc, stencil.scriptData.size(), 3165 stencil.scopeData.size())) { 3166 return false; 3167 } 3168 3169 return atomCache.allocate(fc, stencil.parserAtomData.size()); 3170 } 3171 3172 /* static */ 3173 bool CompilationStencil::prepareForInstantiate( 3174 FrontendContext* fc, const CompilationStencil& stencil, 3175 PreallocatedCompilationGCOutput& gcOutput) { 3176 return gcOutput.allocate(fc, stencil.scriptData.size(), 3177 stencil.scopeData.size()); 3178 } 3179 3180 bool JS::PrepareForInstantiate(JS::FrontendContext* fc, JS::Stencil& stencil, 3181 JS::InstantiationStorage& storage) { 3182 if (!storage.gcOutput_) { 3183 storage.gcOutput_ = 3184 fc->getAllocator() 3185 ->new_<js::frontend::PreallocatedCompilationGCOutput>(); 3186 if (!storage.gcOutput_) { 3187 return false; 3188 } 3189 } 3190 3191 return CompilationStencil::prepareForInstantiate(fc, *stencil.getInitial(), 3192 *storage.gcOutput_); 3193 } 3194 3195 ExtensibleCompilationStencil::ExtensibleCompilationStencil(ScriptSource* source) 3196 : alloc(CompilationStencil::LifoAllocChunkSize, js::BackgroundMallocArena), 3197 source(source), 3198 parserAtoms(alloc) {} 3199 3200 ExtensibleCompilationStencil::ExtensibleCompilationStencil( 3201 CompilationInput& input) 3202 : canLazilyParse(CanLazilyParse(input.options)), 3203 alloc(CompilationStencil::LifoAllocChunkSize, js::BackgroundMallocArena), 3204 source(input.source), 3205 parserAtoms(alloc) {} 3206 3207 ExtensibleCompilationStencil::ExtensibleCompilationStencil( 3208 const JS::ReadOnlyCompileOptions& options, RefPtr<ScriptSource> source) 3209 : canLazilyParse(CanLazilyParse(options)), 3210 alloc(CompilationStencil::LifoAllocChunkSize, js::BackgroundMallocArena), 3211 source(std::move(source)), 3212 parserAtoms(alloc) {} 3213 3214 CompilationState::CompilationState(FrontendContext* fc, 3215 LifoAllocScope& parserAllocScope, 3216 CompilationInput& input) 3217 : ExtensibleCompilationStencil(input), 3218 directives(input.options.forceStrictMode()), 3219 usedNames(fc), 3220 parserAllocScope(parserAllocScope), 3221 input(input) {} 3222 3223 BorrowingCompilationStencil::BorrowingCompilationStencil( 3224 ExtensibleCompilationStencil& extensibleStencil) 3225 : CompilationStencil(extensibleStencil.source) { 3226 storageType = StorageType::Borrowed; 3227 3228 borrowFromExtensibleCompilationStencil(extensibleStencil); 3229 } 3230 3231 SharedDataContainer::~SharedDataContainer() { 3232 if (isEmpty()) { 3233 // Nothing to do. 3234 } else if (isSingle()) { 3235 asSingle()->Release(); 3236 } else if (isVector()) { 3237 js_delete(asVector()); 3238 } else if (isMap()) { 3239 js_delete(asMap()); 3240 } else { 3241 MOZ_ASSERT(isBorrow()); 3242 // Nothing to do. 3243 } 3244 } 3245 3246 bool SharedDataContainer::initVector(FrontendContext* fc) { 3247 MOZ_ASSERT(isEmpty()); 3248 3249 auto* vec = js_new<SharedDataVector>(); 3250 if (!vec) { 3251 ReportOutOfMemory(fc); 3252 return false; 3253 } 3254 data_ = uintptr_t(vec) | VectorTag; 3255 return true; 3256 } 3257 3258 bool SharedDataContainer::initMap(FrontendContext* fc) { 3259 MOZ_ASSERT(isEmpty()); 3260 3261 auto* map = js_new<SharedDataMap>(); 3262 if (!map) { 3263 ReportOutOfMemory(fc); 3264 return false; 3265 } 3266 data_ = uintptr_t(map) | MapTag; 3267 return true; 3268 } 3269 3270 bool SharedDataContainer::prepareStorageFor(FrontendContext* fc, 3271 size_t nonLazyScriptCount, 3272 size_t allScriptCount) { 3273 MOZ_ASSERT(isEmpty()); 3274 3275 if (nonLazyScriptCount <= 1) { 3276 MOZ_ASSERT(isSingle()); 3277 return true; 3278 } 3279 3280 // If the ratio of scripts with bytecode is small, allocating the Vector 3281 // storage with the number of all scripts isn't space-efficient. 3282 // In that case use HashMap instead. 3283 // 3284 // In general, we expect either all scripts to contain bytecode (priviledge 3285 // and self-hosted), or almost none to (eg standard lazy parsing output). 3286 constexpr size_t thresholdRatio = 8; 3287 bool useHashMap = nonLazyScriptCount < allScriptCount / thresholdRatio; 3288 if (useHashMap) { 3289 if (!initMap(fc)) { 3290 return false; 3291 } 3292 if (!asMap()->reserve(nonLazyScriptCount)) { 3293 ReportOutOfMemory(fc); 3294 return false; 3295 } 3296 } else { 3297 if (!initVector(fc)) { 3298 return false; 3299 } 3300 if (!asVector()->resize(allScriptCount)) { 3301 ReportOutOfMemory(fc); 3302 return false; 3303 } 3304 } 3305 3306 return true; 3307 } 3308 3309 bool SharedDataContainer::cloneFrom(FrontendContext* fc, 3310 const SharedDataContainer& other) { 3311 MOZ_ASSERT(isEmpty()); 3312 3313 if (other.isBorrow()) { 3314 return cloneFrom(fc, *other.asBorrow()); 3315 } 3316 3317 if (other.isSingle()) { 3318 // As we clone, we add an extra reference. 3319 RefPtr<SharedImmutableScriptData> ref(other.asSingle()); 3320 setSingle(ref.forget()); 3321 } else if (other.isVector()) { 3322 if (!initVector(fc)) { 3323 return false; 3324 } 3325 if (!asVector()->appendAll(*other.asVector())) { 3326 ReportOutOfMemory(fc); 3327 return false; 3328 } 3329 } else if (other.isMap()) { 3330 if (!initMap(fc)) { 3331 return false; 3332 } 3333 auto& otherMap = *other.asMap(); 3334 if (!asMap()->reserve(otherMap.count())) { 3335 ReportOutOfMemory(fc); 3336 return false; 3337 } 3338 auto& map = *asMap(); 3339 for (auto iter = otherMap.iter(); !iter.done(); iter.next()) { 3340 auto& entry = iter.get(); 3341 map.putNewInfallible(entry.key(), entry.value()); 3342 } 3343 } 3344 return true; 3345 } 3346 3347 js::SharedImmutableScriptData* SharedDataContainer::get( 3348 ScriptIndex index) const { 3349 if (isSingle()) { 3350 if (index == CompilationStencil::TopLevelIndex) { 3351 return asSingle(); 3352 } 3353 return nullptr; 3354 } 3355 3356 if (isVector()) { 3357 auto& vec = *asVector(); 3358 if (index.index < vec.length()) { 3359 return vec[index]; 3360 } 3361 return nullptr; 3362 } 3363 3364 if (isMap()) { 3365 auto& map = *asMap(); 3366 auto p = map.lookup(index); 3367 if (p) { 3368 return p->value(); 3369 } 3370 return nullptr; 3371 } 3372 3373 MOZ_ASSERT(isBorrow()); 3374 return asBorrow()->get(index); 3375 } 3376 3377 bool SharedDataContainer::convertFromSingleToMap(FrontendContext* fc) { 3378 MOZ_ASSERT(isSingle()); 3379 3380 // Use a temporary container so that on OOM we do not break the stencil. 3381 SharedDataContainer other; 3382 if (!other.initMap(fc)) { 3383 return false; 3384 } 3385 3386 if (!other.asMap()->putNew(CompilationStencil::TopLevelIndex, asSingle())) { 3387 ReportOutOfMemory(fc); 3388 return false; 3389 } 3390 3391 std::swap(data_, other.data_); 3392 return true; 3393 } 3394 3395 bool SharedDataContainer::addAndShare(FrontendContext* fc, ScriptIndex index, 3396 js::SharedImmutableScriptData* data) { 3397 MOZ_ASSERT(!isBorrow()); 3398 3399 if (isSingle()) { 3400 MOZ_ASSERT(index == CompilationStencil::TopLevelIndex); 3401 RefPtr<SharedImmutableScriptData> ref(data); 3402 if (!SharedImmutableScriptData::shareScriptData(fc, ref)) { 3403 return false; 3404 } 3405 setSingle(ref.forget()); 3406 return true; 3407 } 3408 3409 if (isVector()) { 3410 auto& vec = *asVector(); 3411 // Resized by SharedDataContainer::prepareStorageFor. 3412 vec[index] = data; 3413 return SharedImmutableScriptData::shareScriptData(fc, vec[index]); 3414 } 3415 3416 MOZ_ASSERT(isMap()); 3417 auto& map = *asMap(); 3418 // Reserved by SharedDataContainer::prepareStorageFor. 3419 map.putNewInfallible(index, data); 3420 auto p = map.lookup(index); 3421 MOZ_ASSERT(p); 3422 return SharedImmutableScriptData::shareScriptData(fc, p->value()); 3423 } 3424 3425 bool SharedDataContainer::addExtraWithoutShare( 3426 FrontendContext* fc, ScriptIndex index, 3427 js::SharedImmutableScriptData* data) { 3428 MOZ_ASSERT(!isEmpty()); 3429 3430 if (isSingle()) { 3431 if (!convertFromSingleToMap(fc)) { 3432 return false; 3433 } 3434 } 3435 3436 if (isVector()) { 3437 // SharedDataContainer::prepareStorageFor allocates space for all scripts. 3438 (*asVector())[index] = data; 3439 return true; 3440 } 3441 3442 MOZ_ASSERT(isMap()); 3443 // SharedDataContainer::prepareStorageFor doesn't allocate space for 3444 // delazification, and this can fail. 3445 if (!asMap()->putNew(index, data)) { 3446 ReportOutOfMemory(fc); 3447 return false; 3448 } 3449 return true; 3450 } 3451 3452 #ifdef DEBUG 3453 void CompilationStencil::assertNoExternalDependency() const { 3454 if (ownedBorrowStencil) { 3455 ownedBorrowStencil->assertNoExternalDependency(); 3456 3457 assertBorrowingFromExtensibleCompilationStencil(*ownedBorrowStencil); 3458 return; 3459 } 3460 3461 MOZ_ASSERT_IF(!scriptData.empty(), alloc.contains(scriptData.data())); 3462 MOZ_ASSERT_IF(!scriptExtra.empty(), alloc.contains(scriptExtra.data())); 3463 3464 MOZ_ASSERT_IF(!scopeData.empty(), alloc.contains(scopeData.data())); 3465 MOZ_ASSERT_IF(!scopeNames.empty(), alloc.contains(scopeNames.data())); 3466 for (const auto* data : scopeNames) { 3467 MOZ_ASSERT_IF(data, alloc.contains(data)); 3468 } 3469 3470 MOZ_ASSERT_IF(!regExpData.empty(), alloc.contains(regExpData.data())); 3471 3472 MOZ_ASSERT_IF(!bigIntData.empty(), alloc.contains(bigIntData.data())); 3473 for (const auto& data : bigIntData) { 3474 MOZ_ASSERT(data.isContainedIn(alloc)); 3475 } 3476 3477 MOZ_ASSERT_IF(!objLiteralData.empty(), alloc.contains(objLiteralData.data())); 3478 for (const auto& data : objLiteralData) { 3479 MOZ_ASSERT(data.isContainedIn(alloc)); 3480 } 3481 3482 MOZ_ASSERT_IF(!parserAtomData.empty(), alloc.contains(parserAtomData.data())); 3483 for (const auto* data : parserAtomData) { 3484 MOZ_ASSERT_IF(data, alloc.contains(data)); 3485 } 3486 3487 MOZ_ASSERT(!sharedData.isBorrow()); 3488 } 3489 3490 void ExtensibleCompilationStencil::assertNoExternalDependency() const { 3491 for (const auto& data : bigIntData) { 3492 MOZ_ASSERT(data.isContainedIn(alloc)); 3493 } 3494 3495 for (const auto& data : objLiteralData) { 3496 MOZ_ASSERT(data.isContainedIn(alloc)); 3497 } 3498 3499 for (const auto* data : scopeNames) { 3500 MOZ_ASSERT_IF(data, alloc.contains(data)); 3501 } 3502 3503 for (const auto* data : parserAtoms.entries()) { 3504 MOZ_ASSERT_IF(data, alloc.contains(data)); 3505 } 3506 3507 MOZ_ASSERT(!sharedData.isBorrow()); 3508 } 3509 #endif // DEBUG 3510 3511 template <typename T, typename VectorT> 3512 [[nodiscard]] bool CopySpanToVector(FrontendContext* fc, VectorT& vec, 3513 mozilla::Span<T>& span) { 3514 auto len = span.size(); 3515 if (len == 0) { 3516 return true; 3517 } 3518 3519 if (!vec.append(span.data(), len)) { 3520 js::ReportOutOfMemory(fc); 3521 return false; 3522 } 3523 return true; 3524 } 3525 3526 template <typename T, typename IntoSpanT, size_t Inline, typename AllocPolicy> 3527 [[nodiscard]] bool CopyToVector(FrontendContext* fc, 3528 mozilla::Vector<T, Inline, AllocPolicy>& vec, 3529 const IntoSpanT& source) { 3530 mozilla::Span<const T> span = source; 3531 return CopySpanToVector(fc, vec, span); 3532 } 3533 3534 // Span and Vector do not share the same method names. 3535 template <typename T, size_t Inline, typename AllocPolicy> 3536 size_t GetLength(const mozilla::Vector<T, Inline, AllocPolicy>& vec) { 3537 return vec.length(); 3538 } 3539 template <typename T> 3540 size_t GetLength(const mozilla::Span<T>& span) { 3541 return span.Length(); 3542 } 3543 3544 // Copy scope names from `src` into `alloc`, and returns the allocated data. 3545 BaseParserScopeData* CopyScopeData(FrontendContext* fc, LifoAlloc& alloc, 3546 ScopeKind kind, 3547 const BaseParserScopeData* src) { 3548 MOZ_ASSERT(kind != ScopeKind::With); 3549 3550 size_t dataSize = SizeOfParserScopeData(kind, src->length); 3551 3552 auto* dest = static_cast<BaseParserScopeData*>(alloc.alloc(dataSize)); 3553 if (!dest) { 3554 js::ReportOutOfMemory(fc); 3555 return nullptr; 3556 } 3557 memcpy(dest, src, dataSize); 3558 3559 return dest; 3560 } 3561 3562 template <typename Stencil> 3563 bool ExtensibleCompilationStencil::cloneFromImpl(FrontendContext* fc, 3564 const Stencil& other) { 3565 MOZ_ASSERT(alloc.isEmpty()); 3566 3567 canLazilyParse = other.canLazilyParse; 3568 functionKey = other.functionKey; 3569 3570 if (!CopyToVector(fc, scriptData, other.scriptData)) { 3571 return false; 3572 } 3573 3574 if (!CopyToVector(fc, scriptExtra, other.scriptExtra)) { 3575 return false; 3576 } 3577 3578 if (!CopyToVector(fc, gcThingData, other.gcThingData)) { 3579 return false; 3580 } 3581 3582 size_t scopeSize = GetLength(other.scopeData); 3583 if (!CopyToVector(fc, scopeData, other.scopeData)) { 3584 return false; 3585 } 3586 if (!scopeNames.reserve(scopeSize)) { 3587 js::ReportOutOfMemory(fc); 3588 return false; 3589 } 3590 for (size_t i = 0; i < scopeSize; i++) { 3591 if (other.scopeNames[i]) { 3592 BaseParserScopeData* data = CopyScopeData( 3593 fc, alloc, other.scopeData[i].kind(), other.scopeNames[i]); 3594 if (!data) { 3595 return false; 3596 } 3597 scopeNames.infallibleEmplaceBack(data); 3598 } else { 3599 scopeNames.infallibleEmplaceBack(nullptr); 3600 } 3601 } 3602 3603 if (!CopyToVector(fc, regExpData, other.regExpData)) { 3604 return false; 3605 } 3606 3607 // If CompilationStencil has external dependency, peform deep copy. 3608 3609 size_t bigIntSize = GetLength(other.bigIntData); 3610 if (!bigIntData.resize(bigIntSize)) { 3611 js::ReportOutOfMemory(fc); 3612 return false; 3613 } 3614 for (size_t i = 0; i < bigIntSize; i++) { 3615 if (!bigIntData[i].init(fc, alloc, other.bigIntData[i])) { 3616 return false; 3617 } 3618 } 3619 3620 size_t objLiteralSize = GetLength(other.objLiteralData); 3621 if (!objLiteralData.reserve(objLiteralSize)) { 3622 js::ReportOutOfMemory(fc); 3623 return false; 3624 } 3625 for (const auto& data : other.objLiteralData) { 3626 size_t length = data.code().size(); 3627 auto* code = alloc.newArrayUninitialized<uint8_t>(length); 3628 if (!code) { 3629 js::ReportOutOfMemory(fc); 3630 return false; 3631 } 3632 memcpy(code, data.code().data(), length); 3633 objLiteralData.infallibleEmplaceBack(code, length, data.kind(), 3634 data.flags(), data.propertyCount()); 3635 } 3636 3637 // Regardless of whether CompilationStencil has external dependency or not, 3638 // ParserAtoms should be interned, to populate internal HashMap. 3639 for (const auto* entry : other.parserAtomsSpan()) { 3640 if (!entry) { 3641 if (!parserAtoms.addPlaceholder(fc)) { 3642 return false; 3643 } 3644 continue; 3645 } 3646 3647 auto index = parserAtoms.internExternalParserAtom(fc, entry); 3648 if (!index) { 3649 return false; 3650 } 3651 } 3652 3653 // We copy the stencil and increment the reference count of each 3654 // SharedImmutableScriptData. 3655 if (!sharedData.cloneFrom(fc, other.sharedData)) { 3656 return false; 3657 } 3658 3659 // Note: moduleMetadata and asmJS are known after the first parse, and are 3660 // not mutated by any delazifications later on. Thus we can safely increment 3661 // the reference counter and keep these as-is. 3662 moduleMetadata = other.moduleMetadata; 3663 asmJS = other.asmJS; 3664 3665 #ifdef DEBUG 3666 assertNoExternalDependency(); 3667 #endif 3668 3669 return true; 3670 } 3671 3672 bool ExtensibleCompilationStencil::cloneFrom(FrontendContext* fc, 3673 const CompilationStencil& other) { 3674 return cloneFromImpl(fc, other); 3675 } 3676 bool ExtensibleCompilationStencil::cloneFrom( 3677 FrontendContext* fc, const ExtensibleCompilationStencil& other) { 3678 return cloneFromImpl(fc, other); 3679 } 3680 3681 bool ExtensibleCompilationStencil::steal(FrontendContext* fc, 3682 RefPtr<CompilationStencil>&& other) { 3683 MOZ_ASSERT(alloc.isEmpty()); 3684 using StorageType = CompilationStencil::StorageType; 3685 StorageType storageType = other->storageType; 3686 if (other->hasMultipleReference()) { 3687 storageType = StorageType::Borrowed; 3688 } 3689 3690 if (storageType == StorageType::OwnedExtensible) { 3691 auto& otherExtensible = other->ownedBorrowStencil; 3692 3693 canLazilyParse = otherExtensible->canLazilyParse; 3694 functionKey = otherExtensible->functionKey; 3695 3696 alloc.steal(&otherExtensible->alloc); 3697 3698 source = std::move(otherExtensible->source); 3699 3700 scriptData = std::move(otherExtensible->scriptData); 3701 scriptExtra = std::move(otherExtensible->scriptExtra); 3702 gcThingData = std::move(otherExtensible->gcThingData); 3703 scopeData = std::move(otherExtensible->scopeData); 3704 scopeNames = std::move(otherExtensible->scopeNames); 3705 regExpData = std::move(otherExtensible->regExpData); 3706 bigIntData = std::move(otherExtensible->bigIntData); 3707 objLiteralData = std::move(otherExtensible->objLiteralData); 3708 3709 parserAtoms = std::move(otherExtensible->parserAtoms); 3710 parserAtoms.fixupAlloc(alloc); 3711 3712 sharedData = std::move(otherExtensible->sharedData); 3713 moduleMetadata = std::move(otherExtensible->moduleMetadata); 3714 asmJS = std::move(otherExtensible->asmJS); 3715 3716 #ifdef DEBUG 3717 assertNoExternalDependency(); 3718 #endif 3719 3720 return true; 3721 } 3722 3723 if (storageType == StorageType::Borrowed) { 3724 return cloneFrom(fc, *other); 3725 } 3726 3727 MOZ_ASSERT(storageType == StorageType::Owned); 3728 3729 canLazilyParse = other->canLazilyParse; 3730 functionKey = other->functionKey; 3731 3732 #ifdef DEBUG 3733 other->assertNoExternalDependency(); 3734 MOZ_ASSERT(!other->hasMultipleReference()); 3735 #endif 3736 3737 // If CompilationStencil has no external dependency, 3738 // steal LifoAlloc and perform shallow copy. 3739 alloc.steal(&other->alloc); 3740 3741 if (!CopySpanToVector(fc, scriptData, other->scriptData)) { 3742 return false; 3743 } 3744 3745 if (!CopySpanToVector(fc, scriptExtra, other->scriptExtra)) { 3746 return false; 3747 } 3748 3749 if (!CopySpanToVector(fc, gcThingData, other->gcThingData)) { 3750 return false; 3751 } 3752 3753 if (!CopySpanToVector(fc, scopeData, other->scopeData)) { 3754 return false; 3755 } 3756 if (!CopySpanToVector(fc, scopeNames, other->scopeNames)) { 3757 return false; 3758 } 3759 3760 if (!CopySpanToVector(fc, regExpData, other->regExpData)) { 3761 return false; 3762 } 3763 3764 if (!CopySpanToVector(fc, bigIntData, other->bigIntData)) { 3765 return false; 3766 } 3767 3768 if (!CopySpanToVector(fc, objLiteralData, other->objLiteralData)) { 3769 return false; 3770 } 3771 3772 // Regardless of whether CompilationStencil has external dependency or not, 3773 // ParserAtoms should be interned, to populate internal HashMap. 3774 for (const auto* entry : other->parserAtomData) { 3775 if (!entry) { 3776 if (!parserAtoms.addPlaceholder(fc)) { 3777 return false; 3778 } 3779 continue; 3780 } 3781 3782 auto index = parserAtoms.internExternalParserAtom(fc, entry); 3783 if (!index) { 3784 return false; 3785 } 3786 } 3787 3788 sharedData = std::move(other->sharedData); 3789 moduleMetadata = std::move(other->moduleMetadata); 3790 asmJS = std::move(other->asmJS); 3791 3792 #ifdef DEBUG 3793 assertNoExternalDependency(); 3794 #endif 3795 3796 return true; 3797 } 3798 3799 bool CompilationStencil::isModule() const { 3800 return scriptExtra[CompilationStencil::TopLevelIndex].isModule(); 3801 } 3802 3803 bool ExtensibleCompilationStencil::isModule() const { 3804 return scriptExtra[CompilationStencil::TopLevelIndex].isModule(); 3805 } 3806 3807 bool CompilationStencil::hasAsmJS() const { return asmJS; } 3808 3809 bool ExtensibleCompilationStencil::hasAsmJS() const { return asmJS; } 3810 3811 bool InitialStencilAndDelazifications::hasAsmJS() const { 3812 return initial_->hasAsmJS(); 3813 } 3814 3815 InitialStencilAndDelazifications::~InitialStencilAndDelazifications() { 3816 MOZ_ASSERT(refCount_ == 0); 3817 3818 for (size_t i = 0; i < delazifications_.length(); i++) { 3819 CompilationStencil* delazification = delazifications_[i].exchange(nullptr); 3820 if (delazification) { 3821 delazification->Release(); 3822 } 3823 } 3824 } 3825 3826 void InitialStencilAndDelazifications::AddRef() { refCount_++; } 3827 3828 void InitialStencilAndDelazifications::Release() { 3829 MOZ_RELEASE_ASSERT(refCount_ > 0); 3830 if (--refCount_ == 0) { 3831 js_delete(this); 3832 } 3833 } 3834 3835 InitialStencilAndDelazifications::RelativeIndexesGuard 3836 InitialStencilAndDelazifications::ensureRelativeIndexes(FrontendContext* fc) { 3837 auto consumersGuard = relativeIndexes_.consumers_.lock(); 3838 if (consumersGuard > 0) { 3839 MOZ_ASSERT(relativeIndexes_.indexes_.length() == 3840 initial_->scriptData.size() - 1); 3841 consumersGuard += 1; 3842 return RelativeIndexesGuard(this); 3843 } 3844 3845 if (!relativeIndexes_.indexes_.resize(initial_->scriptData.size() - 1)) { 3846 ReportOutOfMemory(fc); 3847 return RelativeIndexesGuard(nullptr); 3848 } 3849 3850 auto writeIndex = [&](ScriptIndex index, ScriptIndex enclosingInInitial, 3851 ScriptIndex enclosedInEnclosing) { 3852 MOZ_ASSERT(index > 0); 3853 MOZ_ASSERT( 3854 index == getInitialIndexFor(enclosingInInitial, enclosedInEnclosing), 3855 "getInitialIndexFor does not match relativeIndexes_"); 3856 relativeIndexes_[index - 1] = 3857 ScriptIndexes{enclosingInInitial, enclosedInEnclosing}; 3858 }; 3859 for (size_t index = 0; index < initial_->scriptData.size(); index++) { 3860 auto& scriptData = initial_->scriptData[index]; 3861 // index > 0 is used to iterate over the top-level which is not flagged as 3862 // isInterpreted. 3863 if (index > 0 && 3864 (scriptData.isGhost() || !scriptData.functionFlags.isInterpreted())) { 3865 continue; 3866 } 3867 3868 auto gcthings = scriptData.gcthings(*initial_.get()); 3869 if (scriptData.hasSharedData()) { 3870 // Then the gc things might not have a contiguous array of inner 3871 // functions. Thus we have to iterate over all gcthings. 3872 for (auto gcthing : gcthings) { 3873 if (gcthing.isFunction()) { 3874 writeIndex(gcthing.toFunction(), ScriptIndex(index), 3875 gcthing.toFunction()); 3876 } 3877 } 3878 continue; 3879 } 3880 3881 // The function is syntax-parsed in the initial compilation. In this case, 3882 // the gcthings contains only the list of inner function indices followed by 3883 // the list of closed over bindings. (see 3884 // PerHandlerParser<SyntaxParseHandler>::finishFunction) 3885 3886 // The first element of scriptData and scriptExtra is the compiled script 3887 // it-self, in the upcoming delazification stencil, thus we start after, at 3888 // 1 to index inner functions. 3889 size_t functionIndex = 1; 3890 for (auto gcthing : gcthings) { 3891 if (!gcthing.isFunction()) { 3892 break; 3893 } 3894 writeIndex(gcthing.toFunction(), ScriptIndex(index), 3895 ScriptIndex(functionIndex)); 3896 functionIndex++; 3897 } 3898 } 3899 3900 consumersGuard += 1; 3901 return RelativeIndexesGuard(this); 3902 } 3903 3904 void InitialStencilAndDelazifications::decrementRelativeIndexesConsumer() { 3905 auto consumersGuard = relativeIndexes_.consumers_.lock(); 3906 consumersGuard -= 1; 3907 if (consumersGuard == 0) { 3908 relativeIndexes_.indexes_.clearAndFree(); 3909 } 3910 } 3911 3912 bool InitialStencilAndDelazifications::init(FrontendContext* fc, 3913 const CompilationStencil* initial) { 3914 MOZ_ASSERT(initial->isInitialStencil()); 3915 3916 initial_ = initial; 3917 3918 if (!canLazilyParse()) { 3919 // If the initial stencil is known to be fully-parsed, delazification 3920 // never happens, and the delazifications_ vector and the 3921 // functionKeyToInitialScriptIndex_ map is never used. 3922 return true; 3923 } 3924 3925 if (!delazifications_.resize(initial_->scriptData.size())) { 3926 ReportOutOfMemory(fc); 3927 return false; 3928 } 3929 3930 return functionKeyToInitialScriptIndex_.init(fc, initial_); 3931 } 3932 3933 const CompilationStencil* InitialStencilAndDelazifications::getInitial() const { 3934 return initial_.get(); 3935 } 3936 3937 const CompilationStencil* InitialStencilAndDelazifications::getDelazificationAt( 3938 size_t functionIndex) const { 3939 MOZ_ASSERT(canLazilyParse()); 3940 MOZ_ASSERT(functionIndex > 0); 3941 3942 return delazifications_[functionIndex - 1]; 3943 } 3944 3945 const CompilationStencil* 3946 InitialStencilAndDelazifications::getDelazificationFor( 3947 const SourceExtent& extent) const { 3948 MOZ_ASSERT(canLazilyParse()); 3949 auto maybeIndex = 3950 functionKeyToInitialScriptIndex_.get(extent.toFunctionKey()); 3951 MOZ_ASSERT(maybeIndex, 3952 "The extent parameter should be for a function inside the script"); 3953 return getDelazificationAt(*maybeIndex); 3954 } 3955 3956 ScriptIndex InitialStencilAndDelazifications::getScriptIndexFor( 3957 const CompilationStencil* delazification) const { 3958 MOZ_ASSERT(!delazification->isInitialStencil()); 3959 auto maybeIndex = 3960 functionKeyToInitialScriptIndex_.get(delazification->functionKey); 3961 MOZ_ASSERT(maybeIndex, 3962 "The delazification should be for a function inside the script"); 3963 return *maybeIndex; 3964 } 3965 3966 const ScriptIndexes& InitialStencilAndDelazifications::getRelativeIndexesAt( 3967 ScriptIndex initialIndex) const { 3968 MOZ_ASSERT(initialIndex.index > 0); 3969 3970 return relativeIndexes_[initialIndex.index - 1]; 3971 } 3972 3973 ScriptIndex InitialStencilAndDelazifications::getInitialIndexFor( 3974 ScriptIndex enclosingInInitial, ScriptIndex enclosedInEnclosing) const { 3975 if (enclosedInEnclosing == 0) { 3976 // The first function in a CompilationStencil is it-self. 3977 return enclosingInInitial; 3978 } 3979 3980 auto& scriptDataFromInitial = initial_->scriptData[enclosingInInitial]; 3981 auto gcthings = scriptDataFromInitial.gcthings(*initial_.get()); 3982 // When looking for enclosed script, there is at least one entry in the array 3983 // of gc things. 3984 MOZ_ASSERT(gcthings.Length() > 0); 3985 MOZ_ASSERT(scriptDataFromInitial.hasSharedData() == gcthings[0].isScope()); 3986 if (scriptDataFromInitial.hasSharedData()) { 3987 // The function is already compiled as part of the initial stencil. Thus, 3988 // the enclosedInEnclosing index is already an index in the initial stencil. 3989 return enclosedInEnclosing; 3990 } 3991 3992 // When the functions does not have associated bytecode and scope-chains, 3993 // then we have a contiguous array of inner functions that we can address 3994 // using the enclosed index. 3995 // 3996 // We remove 1 as index 0 corresponds to the enclosing function, which is not 3997 // listed in the set of gc things. 3998 return gcthings[enclosedInEnclosing - 1].toFunction(); 3999 } 4000 4001 const CompilationStencil* InitialStencilAndDelazifications::storeDelazification( 4002 RefPtr<CompilationStencil>&& delazification) { 4003 MOZ_ASSERT(!delazification->hasMultipleReference()); 4004 MOZ_ASSERT(canLazilyParse()); 4005 4006 auto maybeIndex = 4007 functionKeyToInitialScriptIndex_.get(delazification->functionKey); 4008 MOZ_ASSERT(maybeIndex); 4009 size_t functionIndex = *maybeIndex; 4010 4011 CompilationStencil* raw = delazification.forget().take(); 4012 if (delazifications_[functionIndex - 1].compareExchange(nullptr, raw)) { 4013 return raw; 4014 } 4015 4016 #ifdef DEBUG 4017 // The delazification vector already has an entry for the function. This is an 4018 // opportunity to test whether we are generating the same stencils. 4019 { 4020 const CompilationStencil* saved = delazifications_[functionIndex - 1]; 4021 raw->assertNoExternalDependency(); 4022 saved->assertNoExternalDependency(); 4023 MOZ_ASSERT(raw->source == saved->source); 4024 MOZ_ASSERT(raw->scriptData.size() == saved->scriptData.size()); 4025 MOZ_ASSERT(raw->scriptExtra.size() == saved->scriptExtra.size()); 4026 MOZ_ASSERT(raw->gcThingData.size() == saved->gcThingData.size()); 4027 MOZ_ASSERT(raw->scopeData.size() == saved->scopeData.size()); 4028 MOZ_ASSERT(raw->scopeNames.size() == saved->scopeNames.size()); 4029 MOZ_ASSERT(raw->regExpData.size() == saved->regExpData.size()); 4030 MOZ_ASSERT(raw->bigIntData.size() == saved->bigIntData.size()); 4031 MOZ_ASSERT(raw->objLiteralData.size() == saved->objLiteralData.size()); 4032 MOZ_ASSERT(raw->parserAtomData.size() == saved->parserAtomData.size()); 4033 } 4034 #endif 4035 4036 raw->Release(); 4037 return delazifications_[functionIndex - 1]; 4038 } 4039 4040 CompilationStencil* InitialStencilAndDelazifications::getMerged( 4041 FrontendContext* fc) const { 4042 MOZ_ASSERT(canLazilyParse()); 4043 4044 UniquePtr<ExtensibleCompilationStencil> extensibleStencil( 4045 fc->getAllocator()->new_<ExtensibleCompilationStencil>(initial_->source)); 4046 if (!extensibleStencil) { 4047 return nullptr; 4048 } 4049 4050 if (!extensibleStencil->cloneFrom(fc, *initial_)) { 4051 return nullptr; 4052 } 4053 4054 CompilationStencilMerger merger; 4055 if (!merger.setInitial(fc, std::move(extensibleStencil))) { 4056 return nullptr; 4057 } 4058 4059 for (const auto& delazification : delazifications_) { 4060 if (!delazification) { 4061 continue; 4062 } 4063 4064 // NOTE: The delazifications_ vector can be modified by other threads 4065 // during the iteration. 4066 // The enclosing delazification's is not guaranteed to be iterated 4067 // over in this iteration. 4068 // If the enclosing function wasn't merged, all inner functions are 4069 // ignored inside maybeAddDelazification. 4070 if (!merger.maybeAddDelazification(fc, *delazification)) { 4071 return nullptr; 4072 } 4073 } 4074 4075 UniquePtr<ExtensibleCompilationStencil> merged = merger.takeResult(); 4076 return fc->getAllocator()->new_<CompilationStencil>(std::move(merged)); 4077 } 4078 4079 /* static */ 4080 bool InitialStencilAndDelazifications::instantiateStencils( 4081 JSContext* cx, CompilationInput& input, 4082 InitialStencilAndDelazifications& stencils, CompilationGCOutput& gcOutput) { 4083 if (!CompilationStencil::instantiateStencils(cx, input, *stencils.initial_, 4084 gcOutput)) { 4085 return false; 4086 } 4087 4088 if (input.options.populateDelazificationCache()) { 4089 RefPtr<InitialStencilAndDelazifications> stencilsPtr = &stencils; 4090 ScriptSourceObject* sso = gcOutput.script->sourceObject(); 4091 MOZ_ASSERT(!sso->maybeGetStencils()); 4092 if (!stencils.hasAsmJS()) { 4093 sso->setStencils(stencilsPtr.forget()); 4094 sso->setSharingDelazifications(); 4095 } 4096 } 4097 4098 // At this point, gcOutput.script contains the top-level script, and 4099 // gcOutput.functions[i] contains i-th function, where 0-th item is 4100 // always nullptr. 4101 // gcOutput.functions[i]->baseScript() is either JSScript or lazy script. 4102 for (size_t i = 0, length = stencils.delazifications_.length(); i < length; 4103 i++) { 4104 const auto& delazification = stencils.delazifications_[i]; 4105 if (!delazification) { 4106 continue; 4107 } 4108 4109 ScriptIndex scriptIndex = ScriptIndex(i + 1); 4110 JS::Rooted<JSFunction*> fun(cx, gcOutput.functions[scriptIndex]); 4111 if (!fun->baseScript()->isReadyForDelazification()) { 4112 // NOTE: The delazifications_ vector can be modified by other threads 4113 // during the iteration. 4114 // The enclosing delazification's is not guaranteed to be iterated 4115 // over in this iteration. 4116 // If the enclosing function wasn't instantiated, ignore all inner 4117 // functions. 4118 continue; 4119 } 4120 4121 JS::Rooted<CompilationInput> inputForFunc(cx, 4122 CompilationInput(input.options)); 4123 inputForFunc.get().initFromLazy(cx, fun->baseScript(), input.source); 4124 4125 // TODO: The preparation can be shared across iterations. 4126 JS::Rooted<CompilationGCOutput> gcOutputForFunc(cx); 4127 if (!CompilationStencil::instantiateStencils( 4128 cx, inputForFunc.get(), *delazification, gcOutputForFunc.get())) { 4129 return false; 4130 } 4131 } 4132 4133 return true; 4134 } 4135 4136 size_t InitialStencilAndDelazifications::sizeOfExcludingThis( 4137 mozilla::MallocSizeOf mallocSizeOf) const { 4138 size_t size = 0; 4139 4140 if (initial_) { 4141 // The initial stencil can be shared between multiple owners, but 4142 // in most case this instance is considered as the main owner, in term 4143 // of the memory reporting. 4144 size += initial_->sizeOfIncludingThis(mallocSizeOf); 4145 } 4146 4147 size += delazifications_.sizeOfExcludingThis(mallocSizeOf); 4148 4149 for (const auto& delazification : delazifications_) { 4150 if (!delazification) { 4151 continue; 4152 } 4153 4154 // Delazifications are exclusively owned by this instance. 4155 size += (*delazification).sizeOfIncludingThis(mallocSizeOf); 4156 } 4157 4158 size += functionKeyToInitialScriptIndex_.sizeOfExcludingThis(mallocSizeOf); 4159 4160 return size; 4161 } 4162 4163 mozilla::Span<TaggedScriptThingIndex> ScriptStencil::gcthings( 4164 const CompilationStencil& stencil) const { 4165 return stencil.gcThingData.Subspan(gcThingsOffset, gcThingsLength); 4166 } 4167 4168 bool BigIntStencil::initFromChars(FrontendContext* fc, LifoAlloc& alloc, 4169 mozilla::Span<const char16_t> buf) { 4170 MOZ_ASSERT(ParseBigInt64Literal(buf).isNothing(), 4171 "int64-sized BigInts are stored inline"); 4172 4173 size_t length = buf.size(); 4174 MOZ_ASSERT(length > 0); 4175 4176 char16_t* p = alloc.template newArrayUninitialized<char16_t>(length); 4177 if (!p) { 4178 ReportOutOfMemory(fc); 4179 return false; 4180 } 4181 mozilla::PodCopy(p, buf.data(), length); 4182 bigInt_ = mozilla::AsVariant(mozilla::Span(p, length)); 4183 return true; 4184 } 4185 4186 bool BigIntStencil::init(FrontendContext* fc, LifoAlloc& alloc, 4187 mozilla::Span<const char16_t> buf) { 4188 if (auto int64 = ParseBigInt64Literal(buf)) { 4189 bigInt_ = mozilla::AsVariant(*int64); 4190 return true; 4191 } 4192 return initFromChars(fc, alloc, buf); 4193 } 4194 4195 bool BigIntStencil::init(FrontendContext* fc, LifoAlloc& alloc, 4196 const BigIntStencil& other) { 4197 if (other.bigInt_.is<int64_t>()) { 4198 bigInt_ = other.bigInt_; 4199 return true; 4200 } 4201 return initFromChars(fc, alloc, other.source()); 4202 } 4203 4204 BigInt* BigIntStencil::createBigInt(JSContext* cx) const { 4205 return bigInt_.match( 4206 [cx](mozilla::Span<char16_t> source) { 4207 return js::ParseBigIntLiteral(cx, source); 4208 }, 4209 [cx](int64_t int64) { 4210 // BigInts are stored in the script's data vector and therefore need to 4211 // be allocated in the tenured heap. 4212 constexpr gc::Heap heap = gc::Heap::Tenured; 4213 return BigInt::createFromInt64(cx, int64, heap); 4214 }); 4215 } 4216 4217 bool BigIntStencil::isZero() const { 4218 return bigInt_.match([](mozilla::Span<char16_t>) { return false; }, 4219 [](int64_t int64) { return int64 == 0; }); 4220 } 4221 4222 bool BigIntStencil::inplaceNegate() { 4223 return bigInt_.match([](mozilla::Span<char16_t>) { return false; }, 4224 [](int64_t& int64) { 4225 auto negated = -mozilla::CheckedInt<int64_t>{int64}; 4226 if (!negated.isValid()) { 4227 return false; 4228 } 4229 int64 = negated.value(); 4230 return true; 4231 }); 4232 } 4233 4234 bool BigIntStencil::inplaceBitNot() { 4235 return bigInt_.match([](mozilla::Span<char16_t>) { return false; }, 4236 [](int64_t& int64) { 4237 int64 = ~int64; 4238 return true; 4239 }); 4240 } 4241 4242 #ifdef DEBUG 4243 bool BigIntStencil::isContainedIn(const LifoAlloc& alloc) const { 4244 return bigInt_.match( 4245 [&alloc](mozilla::Span<char16_t> source) { 4246 return alloc.contains(source.data()); 4247 }, 4248 [](int64_t) { return true; }); 4249 } 4250 #endif 4251 4252 #if defined(DEBUG) || defined(JS_JITSPEW) 4253 4254 void frontend::DumpTaggedParserAtomIndex(js::JSONPrinter& json, 4255 TaggedParserAtomIndex taggedIndex, 4256 const CompilationStencil* stencil) { 4257 if (taggedIndex.isParserAtomIndex()) { 4258 json.property("tag", "AtomIndex"); 4259 auto index = taggedIndex.toParserAtomIndex(); 4260 if (stencil && stencil->parserAtomData[index]) { 4261 GenericPrinter& out = json.beginStringProperty("atom"); 4262 stencil->parserAtomData[index]->dumpCharsNoQuote(out); 4263 json.endString(); 4264 } else { 4265 json.property("index", size_t(index)); 4266 } 4267 return; 4268 } 4269 4270 if (taggedIndex.isWellKnownAtomId()) { 4271 json.property("tag", "WellKnown"); 4272 auto index = taggedIndex.toWellKnownAtomId(); 4273 switch (index) { 4274 case WellKnownAtomId::empty_: 4275 json.property("atom", ""); 4276 break; 4277 4278 # define CASE_(name, _) case WellKnownAtomId::name: 4279 FOR_EACH_NONTINY_COMMON_PROPERTYNAME(CASE_) 4280 # undef CASE_ 4281 4282 # define CASE_(name, _) case WellKnownAtomId::name: 4283 JS_FOR_EACH_PROTOTYPE(CASE_) 4284 # undef CASE_ 4285 4286 # define CASE_(name) case WellKnownAtomId::name: 4287 JS_FOR_EACH_WELL_KNOWN_SYMBOL(CASE_) 4288 # undef CASE_ 4289 4290 { 4291 GenericPrinter& out = json.beginStringProperty("atom"); 4292 ParserAtomsTable::dumpCharsNoQuote(out, index); 4293 json.endString(); 4294 break; 4295 } 4296 4297 default: 4298 // This includes tiny WellKnownAtomId atoms, which is invalid. 4299 json.property("index", size_t(index)); 4300 break; 4301 } 4302 return; 4303 } 4304 4305 if (taggedIndex.isLength1StaticParserString()) { 4306 json.property("tag", "Length1Static"); 4307 auto index = taggedIndex.toLength1StaticParserString(); 4308 GenericPrinter& out = json.beginStringProperty("atom"); 4309 ParserAtomsTable::dumpCharsNoQuote(out, index); 4310 json.endString(); 4311 return; 4312 } 4313 4314 if (taggedIndex.isLength2StaticParserString()) { 4315 json.property("tag", "Length2Static"); 4316 auto index = taggedIndex.toLength2StaticParserString(); 4317 GenericPrinter& out = json.beginStringProperty("atom"); 4318 ParserAtomsTable::dumpCharsNoQuote(out, index); 4319 json.endString(); 4320 return; 4321 } 4322 4323 if (taggedIndex.isLength3StaticParserString()) { 4324 json.property("tag", "Length3Static"); 4325 auto index = taggedIndex.toLength3StaticParserString(); 4326 GenericPrinter& out = json.beginStringProperty("atom"); 4327 ParserAtomsTable::dumpCharsNoQuote(out, index); 4328 json.endString(); 4329 return; 4330 } 4331 4332 MOZ_ASSERT(taggedIndex.isNull()); 4333 json.property("tag", "null"); 4334 } 4335 4336 void frontend::DumpTaggedParserAtomIndexNoQuote( 4337 GenericPrinter& out, TaggedParserAtomIndex taggedIndex, 4338 const CompilationStencil* stencil) { 4339 if (taggedIndex.isParserAtomIndex()) { 4340 auto index = taggedIndex.toParserAtomIndex(); 4341 if (stencil && stencil->parserAtomData[index]) { 4342 stencil->parserAtomData[index]->dumpCharsNoQuote(out); 4343 } else { 4344 out.printf("AtomIndex#%zu", size_t(index)); 4345 } 4346 return; 4347 } 4348 4349 if (taggedIndex.isWellKnownAtomId()) { 4350 auto index = taggedIndex.toWellKnownAtomId(); 4351 switch (index) { 4352 case WellKnownAtomId::empty_: 4353 out.put("#<zero-length name>"); 4354 break; 4355 4356 # define CASE_(name, _) case WellKnownAtomId::name: 4357 FOR_EACH_NONTINY_COMMON_PROPERTYNAME(CASE_) 4358 # undef CASE_ 4359 4360 # define CASE_(name, _) case WellKnownAtomId::name: 4361 JS_FOR_EACH_PROTOTYPE(CASE_) 4362 # undef CASE_ 4363 4364 # define CASE_(name) case WellKnownAtomId::name: 4365 JS_FOR_EACH_WELL_KNOWN_SYMBOL(CASE_) 4366 # undef CASE_ 4367 4368 { 4369 ParserAtomsTable::dumpCharsNoQuote(out, index); 4370 break; 4371 } 4372 4373 default: 4374 // This includes tiny WellKnownAtomId atoms, which is invalid. 4375 out.printf("WellKnown#%zu", size_t(index)); 4376 break; 4377 } 4378 return; 4379 } 4380 4381 if (taggedIndex.isLength1StaticParserString()) { 4382 auto index = taggedIndex.toLength1StaticParserString(); 4383 ParserAtomsTable::dumpCharsNoQuote(out, index); 4384 return; 4385 } 4386 4387 if (taggedIndex.isLength2StaticParserString()) { 4388 auto index = taggedIndex.toLength2StaticParserString(); 4389 ParserAtomsTable::dumpCharsNoQuote(out, index); 4390 return; 4391 } 4392 4393 if (taggedIndex.isLength3StaticParserString()) { 4394 auto index = taggedIndex.toLength3StaticParserString(); 4395 ParserAtomsTable::dumpCharsNoQuote(out, index); 4396 return; 4397 } 4398 4399 MOZ_ASSERT(taggedIndex.isNull()); 4400 out.put("#<null name>"); 4401 } 4402 4403 void RegExpStencil::dump() const { 4404 js::Fprinter out(stderr); 4405 js::JSONPrinter json(out); 4406 dump(json, nullptr); 4407 } 4408 4409 void RegExpStencil::dump(js::JSONPrinter& json, 4410 const CompilationStencil* stencil) const { 4411 json.beginObject(); 4412 dumpFields(json, stencil); 4413 json.endObject(); 4414 } 4415 4416 void RegExpStencil::dumpFields(js::JSONPrinter& json, 4417 const CompilationStencil* stencil) const { 4418 json.beginObjectProperty("pattern"); 4419 DumpTaggedParserAtomIndex(json, atom_, stencil); 4420 json.endObject(); 4421 4422 GenericPrinter& out = json.beginStringProperty("flags"); 4423 4424 if (flags().global()) { 4425 out.put("g"); 4426 } 4427 if (flags().ignoreCase()) { 4428 out.put("i"); 4429 } 4430 if (flags().multiline()) { 4431 out.put("m"); 4432 } 4433 if (flags().dotAll()) { 4434 out.put("s"); 4435 } 4436 if (flags().unicode()) { 4437 out.put("u"); 4438 } 4439 if (flags().sticky()) { 4440 out.put("y"); 4441 } 4442 4443 json.endStringProperty(); 4444 } 4445 4446 void BigIntStencil::dump() const { 4447 js::Fprinter out(stderr); 4448 js::JSONPrinter json(out); 4449 dump(json); 4450 } 4451 4452 void BigIntStencil::dump(js::JSONPrinter& json) const { 4453 GenericPrinter& out = json.beginString(); 4454 dumpCharsNoQuote(out); 4455 json.endString(); 4456 } 4457 4458 void BigIntStencil::dumpCharsNoQuote(GenericPrinter& out) const { 4459 bigInt_.match( 4460 [&out](mozilla::Span<char16_t> source) { 4461 for (char16_t c : source) { 4462 out.putChar(char(c)); 4463 } 4464 }, 4465 [&out](int64_t int64) { out.printf("%" PRId64, int64); }); 4466 } 4467 4468 void ScopeStencil::dump() const { 4469 js::Fprinter out(stderr); 4470 js::JSONPrinter json(out); 4471 dump(json, nullptr, nullptr); 4472 } 4473 4474 void ScopeStencil::dump(js::JSONPrinter& json, 4475 const BaseParserScopeData* baseScopeData, 4476 const CompilationStencil* stencil) const { 4477 json.beginObject(); 4478 dumpFields(json, baseScopeData, stencil); 4479 json.endObject(); 4480 } 4481 4482 void ScopeStencil::dumpFields(js::JSONPrinter& json, 4483 const BaseParserScopeData* baseScopeData, 4484 const CompilationStencil* stencil) const { 4485 json.property("kind", ScopeKindString(kind_)); 4486 4487 if (hasEnclosing()) { 4488 json.formatProperty("enclosing", "ScopeIndex(%zu)", size_t(enclosing())); 4489 } 4490 4491 json.property("firstFrameSlot", firstFrameSlot_); 4492 4493 if (hasEnvironmentShape()) { 4494 json.formatProperty("numEnvironmentSlots", "%zu", 4495 size_t(numEnvironmentSlots_)); 4496 } 4497 4498 if (isFunction()) { 4499 json.formatProperty("functionIndex", "ScriptIndex(%zu)", 4500 size_t(functionIndex_)); 4501 } 4502 4503 json.beginListProperty("flags"); 4504 if (flags_ & HasEnclosing) { 4505 json.value("HasEnclosing"); 4506 } 4507 if (flags_ & HasEnvironmentShape) { 4508 json.value("HasEnvironmentShape"); 4509 } 4510 if (flags_ & IsArrow) { 4511 json.value("IsArrow"); 4512 } 4513 json.endList(); 4514 4515 if (!baseScopeData) { 4516 return; 4517 } 4518 4519 json.beginObjectProperty("data"); 4520 4521 mozilla::Span<const ParserBindingName> trailingNames; 4522 switch (kind_) { 4523 case ScopeKind::Function: { 4524 const auto* data = 4525 static_cast<const FunctionScope::ParserData*>(baseScopeData); 4526 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot); 4527 json.property("hasParameterExprs", data->slotInfo.hasParameterExprs()); 4528 json.property("nonPositionalFormalStart", 4529 data->slotInfo.nonPositionalFormalStart); 4530 json.property("varStart", data->slotInfo.varStart); 4531 4532 trailingNames = GetScopeDataTrailingNames(data); 4533 break; 4534 } 4535 4536 case ScopeKind::FunctionBodyVar: { 4537 const auto* data = 4538 static_cast<const VarScope::ParserData*>(baseScopeData); 4539 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot); 4540 4541 trailingNames = GetScopeDataTrailingNames(data); 4542 break; 4543 } 4544 4545 case ScopeKind::Lexical: 4546 case ScopeKind::SimpleCatch: 4547 case ScopeKind::Catch: 4548 case ScopeKind::NamedLambda: 4549 case ScopeKind::StrictNamedLambda: 4550 case ScopeKind::FunctionLexical: { 4551 const auto* data = 4552 static_cast<const LexicalScope::ParserData*>(baseScopeData); 4553 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot); 4554 json.property("constStart", data->slotInfo.constStart); 4555 4556 trailingNames = GetScopeDataTrailingNames(data); 4557 break; 4558 } 4559 4560 case ScopeKind::ClassBody: { 4561 const auto* data = 4562 static_cast<const ClassBodyScope::ParserData*>(baseScopeData); 4563 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot); 4564 json.property("privateMethodStart", data->slotInfo.privateMethodStart); 4565 4566 trailingNames = GetScopeDataTrailingNames(data); 4567 break; 4568 } 4569 4570 case ScopeKind::With: { 4571 break; 4572 } 4573 4574 case ScopeKind::Eval: 4575 case ScopeKind::StrictEval: { 4576 const auto* data = 4577 static_cast<const EvalScope::ParserData*>(baseScopeData); 4578 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot); 4579 4580 trailingNames = GetScopeDataTrailingNames(data); 4581 break; 4582 } 4583 4584 case ScopeKind::Global: 4585 case ScopeKind::NonSyntactic: { 4586 const auto* data = 4587 static_cast<const GlobalScope::ParserData*>(baseScopeData); 4588 json.property("letStart", data->slotInfo.letStart); 4589 json.property("constStart", data->slotInfo.constStart); 4590 4591 trailingNames = GetScopeDataTrailingNames(data); 4592 break; 4593 } 4594 4595 case ScopeKind::Module: { 4596 const auto* data = 4597 static_cast<const ModuleScope::ParserData*>(baseScopeData); 4598 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot); 4599 json.property("varStart", data->slotInfo.varStart); 4600 json.property("letStart", data->slotInfo.letStart); 4601 json.property("constStart", data->slotInfo.constStart); 4602 4603 trailingNames = GetScopeDataTrailingNames(data); 4604 break; 4605 } 4606 4607 case ScopeKind::WasmInstance: { 4608 const auto* data = 4609 static_cast<const WasmInstanceScope::ParserData*>(baseScopeData); 4610 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot); 4611 json.property("globalsStart", data->slotInfo.globalsStart); 4612 4613 trailingNames = GetScopeDataTrailingNames(data); 4614 break; 4615 } 4616 4617 case ScopeKind::WasmFunction: { 4618 const auto* data = 4619 static_cast<const WasmFunctionScope::ParserData*>(baseScopeData); 4620 json.property("nextFrameSlot", data->slotInfo.nextFrameSlot); 4621 4622 trailingNames = GetScopeDataTrailingNames(data); 4623 break; 4624 } 4625 4626 default: { 4627 MOZ_CRASH("Unexpected ScopeKind"); 4628 break; 4629 } 4630 } 4631 4632 if (!trailingNames.empty()) { 4633 char index[64]; 4634 json.beginObjectProperty("trailingNames"); 4635 for (size_t i = 0; i < trailingNames.size(); i++) { 4636 const auto& name = trailingNames[i]; 4637 SprintfLiteral(index, "%zu", i); 4638 json.beginObjectProperty(index); 4639 4640 json.boolProperty("closedOver", name.closedOver()); 4641 4642 json.boolProperty("isTopLevelFunction", name.isTopLevelFunction()); 4643 4644 json.beginObjectProperty("name"); 4645 DumpTaggedParserAtomIndex(json, name.name(), stencil); 4646 json.endObject(); 4647 4648 json.endObject(); 4649 } 4650 json.endObject(); 4651 } 4652 4653 json.endObject(); 4654 } 4655 4656 static void DumpModuleRequestVectorItems( 4657 js::JSONPrinter& json, const StencilModuleMetadata::RequestVector& requests, 4658 const CompilationStencil* stencil) { 4659 for (const auto& request : requests) { 4660 json.beginObject(); 4661 if (request.specifier) { 4662 json.beginObjectProperty("specifier"); 4663 DumpTaggedParserAtomIndex(json, request.specifier, stencil); 4664 json.endObject(); 4665 } 4666 json.endObject(); 4667 } 4668 } 4669 4670 static void DumpModuleEntryVectorItems( 4671 js::JSONPrinter& json, const StencilModuleMetadata::EntryVector& entries, 4672 const CompilationStencil* stencil) { 4673 for (const auto& entry : entries) { 4674 json.beginObject(); 4675 if (entry.moduleRequest) { 4676 json.property("moduleRequest", entry.moduleRequest.value()); 4677 } 4678 if (entry.localName) { 4679 json.beginObjectProperty("localName"); 4680 DumpTaggedParserAtomIndex(json, entry.localName, stencil); 4681 json.endObject(); 4682 } 4683 if (entry.importName) { 4684 json.beginObjectProperty("importName"); 4685 DumpTaggedParserAtomIndex(json, entry.importName, stencil); 4686 json.endObject(); 4687 } 4688 if (entry.exportName) { 4689 json.beginObjectProperty("exportName"); 4690 DumpTaggedParserAtomIndex(json, entry.exportName, stencil); 4691 json.endObject(); 4692 } 4693 // TODO: Dump assertions. 4694 json.endObject(); 4695 } 4696 } 4697 4698 void StencilModuleMetadata::dump() const { 4699 js::Fprinter out(stderr); 4700 js::JSONPrinter json(out); 4701 dump(json, nullptr); 4702 } 4703 4704 void StencilModuleMetadata::dump(js::JSONPrinter& json, 4705 const CompilationStencil* stencil) const { 4706 json.beginObject(); 4707 dumpFields(json, stencil); 4708 json.endObject(); 4709 } 4710 4711 void StencilModuleMetadata::dumpFields( 4712 js::JSONPrinter& json, const CompilationStencil* stencil) const { 4713 json.beginListProperty("moduleRequests"); 4714 DumpModuleRequestVectorItems(json, moduleRequests, stencil); 4715 json.endList(); 4716 4717 json.beginListProperty("requestedModules"); 4718 DumpModuleEntryVectorItems(json, requestedModules, stencil); 4719 json.endList(); 4720 4721 json.beginListProperty("importEntries"); 4722 DumpModuleEntryVectorItems(json, importEntries, stencil); 4723 json.endList(); 4724 4725 json.beginListProperty("localExportEntries"); 4726 DumpModuleEntryVectorItems(json, localExportEntries, stencil); 4727 json.endList(); 4728 4729 json.beginListProperty("indirectExportEntries"); 4730 DumpModuleEntryVectorItems(json, indirectExportEntries, stencil); 4731 json.endList(); 4732 4733 json.beginListProperty("starExportEntries"); 4734 DumpModuleEntryVectorItems(json, starExportEntries, stencil); 4735 json.endList(); 4736 4737 json.beginListProperty("functionDecls"); 4738 for (const auto& index : functionDecls) { 4739 json.value("ScriptIndex(%zu)", size_t(index)); 4740 } 4741 json.endList(); 4742 4743 json.boolProperty("isAsync", isAsync); 4744 } 4745 4746 void js::DumpImmutableScriptFlags(js::JSONPrinter& json, 4747 ImmutableScriptFlags immutableFlags) { 4748 for (uint32_t i = 1; i; i = i << 1) { 4749 if (uint32_t(immutableFlags) & i) { 4750 switch (ImmutableScriptFlagsEnum(i)) { 4751 case ImmutableScriptFlagsEnum::IsForEval: 4752 json.value("IsForEval"); 4753 break; 4754 case ImmutableScriptFlagsEnum::IsModule: 4755 json.value("IsModule"); 4756 break; 4757 case ImmutableScriptFlagsEnum::IsFunction: 4758 json.value("IsFunction"); 4759 break; 4760 case ImmutableScriptFlagsEnum::SelfHosted: 4761 json.value("SelfHosted"); 4762 break; 4763 case ImmutableScriptFlagsEnum::ForceStrict: 4764 json.value("ForceStrict"); 4765 break; 4766 case ImmutableScriptFlagsEnum::HasNonSyntacticScope: 4767 json.value("HasNonSyntacticScope"); 4768 break; 4769 case ImmutableScriptFlagsEnum::NoScriptRval: 4770 json.value("NoScriptRval"); 4771 break; 4772 case ImmutableScriptFlagsEnum::TreatAsRunOnce: 4773 json.value("TreatAsRunOnce"); 4774 break; 4775 case ImmutableScriptFlagsEnum::Strict: 4776 json.value("Strict"); 4777 break; 4778 case ImmutableScriptFlagsEnum::HasModuleGoal: 4779 json.value("HasModuleGoal"); 4780 break; 4781 case ImmutableScriptFlagsEnum::HasInnerFunctions: 4782 json.value("HasInnerFunctions"); 4783 break; 4784 case ImmutableScriptFlagsEnum::HasDirectEval: 4785 json.value("HasDirectEval"); 4786 break; 4787 case ImmutableScriptFlagsEnum::BindingsAccessedDynamically: 4788 json.value("BindingsAccessedDynamically"); 4789 break; 4790 case ImmutableScriptFlagsEnum::HasCallSiteObj: 4791 json.value("HasCallSiteObj"); 4792 break; 4793 case ImmutableScriptFlagsEnum::IsAsync: 4794 json.value("IsAsync"); 4795 break; 4796 case ImmutableScriptFlagsEnum::IsGenerator: 4797 json.value("IsGenerator"); 4798 break; 4799 case ImmutableScriptFlagsEnum::FunHasExtensibleScope: 4800 json.value("FunHasExtensibleScope"); 4801 break; 4802 case ImmutableScriptFlagsEnum::FunctionHasThisBinding: 4803 json.value("FunctionHasThisBinding"); 4804 break; 4805 case ImmutableScriptFlagsEnum::NeedsHomeObject: 4806 json.value("NeedsHomeObject"); 4807 break; 4808 case ImmutableScriptFlagsEnum::IsDerivedClassConstructor: 4809 json.value("IsDerivedClassConstructor"); 4810 break; 4811 case ImmutableScriptFlagsEnum::IsSyntheticFunction: 4812 json.value("IsSyntheticFunction"); 4813 break; 4814 case ImmutableScriptFlagsEnum::UseMemberInitializers: 4815 json.value("UseMemberInitializers"); 4816 break; 4817 case ImmutableScriptFlagsEnum::HasRest: 4818 json.value("HasRest"); 4819 break; 4820 case ImmutableScriptFlagsEnum::NeedsFunctionEnvironmentObjects: 4821 json.value("NeedsFunctionEnvironmentObjects"); 4822 break; 4823 case ImmutableScriptFlagsEnum::FunctionHasExtraBodyVarScope: 4824 json.value("FunctionHasExtraBodyVarScope"); 4825 break; 4826 case ImmutableScriptFlagsEnum::ShouldDeclareArguments: 4827 json.value("ShouldDeclareArguments"); 4828 break; 4829 case ImmutableScriptFlagsEnum::NeedsArgsObj: 4830 json.value("NeedsArgsObj"); 4831 break; 4832 case ImmutableScriptFlagsEnum::HasMappedArgsObj: 4833 json.value("HasMappedArgsObj"); 4834 break; 4835 case ImmutableScriptFlagsEnum::IsInlinableLargeFunction: 4836 json.value("IsInlinableLargeFunction"); 4837 break; 4838 case ImmutableScriptFlagsEnum::FunctionHasNewTargetBinding: 4839 json.value("FunctionHasNewTargetBinding"); 4840 break; 4841 case ImmutableScriptFlagsEnum::UsesArgumentsIntrinsics: 4842 json.value("UsesArgumentsIntrinsics"); 4843 break; 4844 default: 4845 json.value("Unknown(%x)", i); 4846 break; 4847 } 4848 } 4849 } 4850 } 4851 4852 void js::DumpFunctionFlagsItems(js::JSONPrinter& json, 4853 FunctionFlags functionFlags) { 4854 switch (functionFlags.kind()) { 4855 case FunctionFlags::FunctionKind::NormalFunction: 4856 json.value("NORMAL_KIND"); 4857 break; 4858 case FunctionFlags::FunctionKind::AsmJS: 4859 json.value("ASMJS_KIND"); 4860 break; 4861 case FunctionFlags::FunctionKind::Wasm: 4862 json.value("WASM_KIND"); 4863 break; 4864 case FunctionFlags::FunctionKind::Arrow: 4865 json.value("ARROW_KIND"); 4866 break; 4867 case FunctionFlags::FunctionKind::Method: 4868 json.value("METHOD_KIND"); 4869 break; 4870 case FunctionFlags::FunctionKind::ClassConstructor: 4871 json.value("CLASSCONSTRUCTOR_KIND"); 4872 break; 4873 case FunctionFlags::FunctionKind::Getter: 4874 json.value("GETTER_KIND"); 4875 break; 4876 case FunctionFlags::FunctionKind::Setter: 4877 json.value("SETTER_KIND"); 4878 break; 4879 default: 4880 json.value("Unknown(%x)", uint8_t(functionFlags.kind())); 4881 break; 4882 } 4883 4884 static_assert(FunctionFlags::FUNCTION_KIND_MASK == 0x0007, 4885 "FunctionKind should use the lowest 3 bits"); 4886 for (uint16_t i = 1 << 3; i; i = i << 1) { 4887 if (functionFlags.toRaw() & i) { 4888 switch (FunctionFlags::Flags(i)) { 4889 case FunctionFlags::Flags::EXTENDED: 4890 json.value("EXTENDED"); 4891 break; 4892 case FunctionFlags::Flags::SELF_HOSTED: 4893 json.value("SELF_HOSTED"); 4894 break; 4895 case FunctionFlags::Flags::BASESCRIPT: 4896 json.value("BASESCRIPT"); 4897 break; 4898 case FunctionFlags::Flags::SELFHOSTLAZY: 4899 json.value("SELFHOSTLAZY"); 4900 break; 4901 case FunctionFlags::Flags::CONSTRUCTOR: 4902 json.value("CONSTRUCTOR"); 4903 break; 4904 case FunctionFlags::Flags::LAZY_ACCESSOR_NAME: 4905 json.value("LAZY_ACCESSOR_NAME"); 4906 break; 4907 case FunctionFlags::Flags::LAMBDA: 4908 json.value("LAMBDA"); 4909 break; 4910 case FunctionFlags::Flags::NATIVE_JIT_ENTRY: 4911 json.value("NATIVE_JIT_ENTRY"); 4912 break; 4913 case FunctionFlags::Flags::HAS_INFERRED_NAME: 4914 json.value("HAS_INFERRED_NAME"); 4915 break; 4916 case FunctionFlags::Flags::HAS_GUESSED_ATOM: 4917 json.value("HAS_GUESSED_ATOM"); 4918 break; 4919 case FunctionFlags::Flags::RESOLVED_NAME: 4920 json.value("RESOLVED_NAME"); 4921 break; 4922 case FunctionFlags::Flags::RESOLVED_LENGTH: 4923 json.value("RESOLVED_LENGTH"); 4924 break; 4925 case FunctionFlags::Flags::GHOST_FUNCTION: 4926 json.value("GHOST_FUNCTION"); 4927 break; 4928 default: 4929 json.value("Unknown(%x)", i); 4930 break; 4931 } 4932 } 4933 } 4934 } 4935 4936 static void DumpScriptThing(js::JSONPrinter& json, 4937 const CompilationStencil* stencil, 4938 TaggedScriptThingIndex thing) { 4939 switch (thing.tag()) { 4940 case TaggedScriptThingIndex::Kind::ParserAtomIndex: 4941 case TaggedScriptThingIndex::Kind::WellKnown: 4942 json.beginObject(); 4943 json.property("type", "Atom"); 4944 DumpTaggedParserAtomIndex(json, thing.toAtom(), stencil); 4945 json.endObject(); 4946 break; 4947 case TaggedScriptThingIndex::Kind::Null: 4948 json.nullValue(); 4949 break; 4950 case TaggedScriptThingIndex::Kind::BigInt: 4951 json.value("BigIntIndex(%zu)", size_t(thing.toBigInt())); 4952 break; 4953 case TaggedScriptThingIndex::Kind::ObjLiteral: 4954 json.value("ObjLiteralIndex(%zu)", size_t(thing.toObjLiteral())); 4955 break; 4956 case TaggedScriptThingIndex::Kind::RegExp: 4957 json.value("RegExpIndex(%zu)", size_t(thing.toRegExp())); 4958 break; 4959 case TaggedScriptThingIndex::Kind::Scope: 4960 json.value("ScopeIndex(%zu)", size_t(thing.toScope())); 4961 break; 4962 case TaggedScriptThingIndex::Kind::Function: 4963 json.value("ScriptIndex(%zu)", size_t(thing.toFunction())); 4964 break; 4965 case TaggedScriptThingIndex::Kind::EmptyGlobalScope: 4966 json.value("EmptyGlobalScope"); 4967 break; 4968 } 4969 } 4970 4971 void ScriptStencil::dump() const { 4972 js::Fprinter out(stderr); 4973 js::JSONPrinter json(out); 4974 dump(json, nullptr); 4975 } 4976 4977 void ScriptStencil::dump(js::JSONPrinter& json, 4978 const CompilationStencil* stencil) const { 4979 json.beginObject(); 4980 dumpFields(json, stencil); 4981 json.endObject(); 4982 } 4983 4984 void ScriptStencil::dumpFields(js::JSONPrinter& json, 4985 const CompilationStencil* stencil) const { 4986 json.formatProperty("gcThingsOffset", "CompilationGCThingIndex(%u)", 4987 gcThingsOffset.index); 4988 json.property("gcThingsLength", gcThingsLength); 4989 4990 if (stencil) { 4991 json.beginListProperty("gcThings"); 4992 for (const auto& thing : gcthings(*stencil)) { 4993 DumpScriptThing(json, stencil, thing); 4994 } 4995 json.endList(); 4996 } 4997 4998 json.beginListProperty("flags"); 4999 if (flags_ & WasEmittedByEnclosingScriptFlag) { 5000 json.value("WasEmittedByEnclosingScriptFlag"); 5001 } 5002 if (flags_ & AllowRelazifyFlag) { 5003 json.value("AllowRelazifyFlag"); 5004 } 5005 if (flags_ & HasSharedDataFlag) { 5006 json.value("HasSharedDataFlag"); 5007 } 5008 if (flags_ & HasLazyFunctionEnclosingScopeIndexFlag) { 5009 json.value("HasLazyFunctionEnclosingScopeIndexFlag"); 5010 } 5011 json.endList(); 5012 5013 if (isFunction()) { 5014 json.beginObjectProperty("functionAtom"); 5015 DumpTaggedParserAtomIndex(json, functionAtom, stencil); 5016 json.endObject(); 5017 5018 json.beginListProperty("functionFlags"); 5019 DumpFunctionFlagsItems(json, functionFlags); 5020 json.endList(); 5021 5022 if (hasLazyFunctionEnclosingScopeIndex()) { 5023 json.formatProperty("lazyFunctionEnclosingScopeIndex", "ScopeIndex(%zu)", 5024 size_t(lazyFunctionEnclosingScopeIndex())); 5025 } 5026 5027 if (hasSelfHostedCanonicalName()) { 5028 json.beginObjectProperty("selfHostCanonicalName"); 5029 DumpTaggedParserAtomIndex(json, selfHostedCanonicalName(), stencil); 5030 json.endObject(); 5031 } 5032 } 5033 } 5034 5035 void ScriptStencilExtra::dump() const { 5036 js::Fprinter out(stderr); 5037 js::JSONPrinter json(out); 5038 dump(json); 5039 } 5040 5041 void ScriptStencilExtra::dump(js::JSONPrinter& json) const { 5042 json.beginObject(); 5043 dumpFields(json); 5044 json.endObject(); 5045 } 5046 5047 void ScriptStencilExtra::dumpFields(js::JSONPrinter& json) const { 5048 json.beginListProperty("immutableFlags"); 5049 DumpImmutableScriptFlags(json, immutableFlags); 5050 json.endList(); 5051 5052 json.beginObjectProperty("extent"); 5053 json.property("sourceStart", extent.sourceStart); 5054 json.property("sourceEnd", extent.sourceEnd); 5055 json.property("toStringStart", extent.toStringStart); 5056 json.property("toStringEnd", extent.toStringEnd); 5057 json.property("lineno", extent.lineno); 5058 json.property("column", extent.column.oneOriginValue()); 5059 json.endObject(); 5060 5061 json.property("memberInitializers", memberInitializers_); 5062 5063 json.property("nargs", nargs); 5064 } 5065 5066 void SharedDataContainer::dump() const { 5067 js::Fprinter out(stderr); 5068 js::JSONPrinter json(out); 5069 dump(json); 5070 } 5071 5072 void SharedDataContainer::dump(js::JSONPrinter& json) const { 5073 json.beginObject(); 5074 dumpFields(json); 5075 json.endObject(); 5076 } 5077 5078 void SharedDataContainer::dumpFields(js::JSONPrinter& json) const { 5079 if (isEmpty()) { 5080 json.nullProperty("ScriptIndex(0)"); 5081 return; 5082 } 5083 5084 if (isSingle()) { 5085 json.formatProperty("ScriptIndex(0)", "u8[%zu]", 5086 asSingle()->immutableDataLength()); 5087 return; 5088 } 5089 5090 if (isVector()) { 5091 auto& vec = *asVector(); 5092 5093 char index[64]; 5094 for (size_t i = 0; i < vec.length(); i++) { 5095 SprintfLiteral(index, "ScriptIndex(%zu)", i); 5096 if (vec[i]) { 5097 json.formatProperty(index, "u8[%zu]", vec[i]->immutableDataLength()); 5098 } else { 5099 json.nullProperty(index); 5100 } 5101 } 5102 return; 5103 } 5104 5105 if (isMap()) { 5106 auto& map = *asMap(); 5107 5108 char index[64]; 5109 for (auto iter = map.iter(); !iter.done(); iter.next()) { 5110 SprintfLiteral(index, "ScriptIndex(%u)", iter.get().key().index); 5111 json.formatProperty(index, "u8[%zu]", 5112 iter.get().value()->immutableDataLength()); 5113 } 5114 return; 5115 } 5116 5117 MOZ_ASSERT(isBorrow()); 5118 asBorrow()->dumpFields(json); 5119 } 5120 5121 struct DumpOptionsFields { 5122 js::JSONPrinter& json; 5123 5124 void operator()(const char* name, JS::AsmJSOption value) { 5125 const char* valueStr = nullptr; 5126 switch (value) { 5127 case JS::AsmJSOption::Enabled: 5128 valueStr = "JS::AsmJSOption::Enabled"; 5129 break; 5130 case JS::AsmJSOption::DisabledByAsmJSPref: 5131 valueStr = "JS::AsmJSOption::DisabledByAsmJSPref"; 5132 break; 5133 case JS::AsmJSOption::DisabledByLinker: 5134 valueStr = "JS::AsmJSOption::DisabledByLinker"; 5135 break; 5136 case JS::AsmJSOption::DisabledByNoWasmCompiler: 5137 valueStr = "JS::AsmJSOption::DisabledByNoWasmCompiler"; 5138 break; 5139 case JS::AsmJSOption::DisabledByDebugger: 5140 valueStr = "JS::AsmJSOption::DisabledByDebugger"; 5141 break; 5142 } 5143 json.property(name, valueStr); 5144 } 5145 5146 void operator()(const char* name, JS::DelazificationOption value) { 5147 const char* valueStr = nullptr; 5148 switch (value) { 5149 # define SelectValueStr_(Strategy) \ 5150 case JS::DelazificationOption::Strategy: \ 5151 valueStr = "JS::DelazificationOption::" #Strategy; \ 5152 break; 5153 5154 FOREACH_DELAZIFICATION_STRATEGY(SelectValueStr_) 5155 # undef SelectValueStr_ 5156 } 5157 json.property(name, valueStr); 5158 } 5159 5160 void operator()(const char* name, JS::EagerBaselineOption value) { 5161 const char* valueStr = nullptr; 5162 switch (value) { 5163 case JS::EagerBaselineOption::None: 5164 valueStr = "JS::EagerBaselineOption::None"; 5165 break; 5166 case JS::EagerBaselineOption::JitHints: 5167 valueStr = "JS::EagerBaselineOption::JitHints"; 5168 break; 5169 case JS::EagerBaselineOption::Aggressive: 5170 valueStr = "JS::EagerBaselineOption::Aggressive"; 5171 break; 5172 default: 5173 MOZ_CRASH("Unknown JS::EagerBaselineOption enum"); 5174 break; 5175 } 5176 json.property(name, valueStr); 5177 } 5178 5179 void operator()(const char* name, char16_t* value) {} 5180 5181 void operator()(const char* name, bool value) { json.property(name, value); } 5182 5183 void operator()(const char* name, uint32_t value) { 5184 json.property(name, value); 5185 } 5186 5187 void operator()(const char* name, uint64_t value) { 5188 json.property(name, value); 5189 } 5190 5191 void operator()(const char* name, const char* value) { 5192 if (value) { 5193 json.property(name, value); 5194 return; 5195 } 5196 json.nullProperty(name); 5197 } 5198 5199 void operator()(const char* name, JS::ConstUTF8CharsZ value) { 5200 if (value) { 5201 json.property(name, value.c_str()); 5202 return; 5203 } 5204 json.nullProperty(name); 5205 } 5206 }; 5207 5208 static void DumpOptionsFields(js::JSONPrinter& json, 5209 const JS::ReadOnlyCompileOptions& options) { 5210 struct DumpOptionsFields printer{json}; 5211 options.dumpWith(printer); 5212 } 5213 5214 static void DumpInputScopeFields(js::JSONPrinter& json, 5215 const InputScope& scope) { 5216 json.property("kind", ScopeKindString(scope.kind())); 5217 5218 InputScope enclosing = scope.enclosing(); 5219 if (enclosing.isNull()) { 5220 json.nullProperty("enclosing"); 5221 } else { 5222 json.beginObjectProperty("enclosing"); 5223 DumpInputScopeFields(json, enclosing); 5224 json.endObject(); 5225 } 5226 } 5227 5228 static void DumpInputScriptFields(js::JSONPrinter& json, 5229 const InputScript& script) { 5230 json.beginObjectProperty("extent"); 5231 { 5232 SourceExtent extent = script.extent(); 5233 json.property("sourceStart", extent.sourceStart); 5234 json.property("sourceEnd", extent.sourceEnd); 5235 json.property("toStringStart", extent.toStringStart); 5236 json.property("toStringEnd", extent.toStringEnd); 5237 json.property("lineno", extent.lineno); 5238 json.property("column", extent.column.oneOriginValue()); 5239 } 5240 json.endObject(); 5241 5242 json.beginListProperty("immutableFlags"); 5243 DumpImmutableScriptFlags(json, script.immutableFlags()); 5244 json.endList(); 5245 5246 json.beginListProperty("functionFlags"); 5247 DumpFunctionFlagsItems(json, script.functionFlags()); 5248 json.endList(); 5249 5250 json.property("hasPrivateScriptData", script.hasPrivateScriptData()); 5251 5252 InputScope scope = script.enclosingScope(); 5253 if (scope.isNull()) { 5254 json.nullProperty("enclosingScope"); 5255 } else { 5256 json.beginObjectProperty("enclosingScope"); 5257 DumpInputScopeFields(json, scope); 5258 json.endObject(); 5259 } 5260 5261 if (script.useMemberInitializers()) { 5262 json.property("memberInitializers", 5263 script.getMemberInitializers().serialize()); 5264 } 5265 } 5266 5267 void CompilationInput::dump() const { 5268 js::Fprinter out(stderr); 5269 js::JSONPrinter json(out); 5270 dump(json); 5271 out.put("\n"); 5272 } 5273 5274 void CompilationInput::dump(js::JSONPrinter& json) const { 5275 json.beginObject(); 5276 dumpFields(json); 5277 json.endObject(); 5278 } 5279 5280 void CompilationInput::dumpFields(js::JSONPrinter& json) const { 5281 const char* targetStr = nullptr; 5282 switch (target) { 5283 case CompilationTarget::Global: 5284 targetStr = "CompilationTarget::Global"; 5285 break; 5286 case CompilationTarget::SelfHosting: 5287 targetStr = "CompilationTarget::SelfHosting"; 5288 break; 5289 case CompilationTarget::StandaloneFunction: 5290 targetStr = "CompilationTarget::StandaloneFunction"; 5291 break; 5292 case CompilationTarget::StandaloneFunctionInNonSyntacticScope: 5293 targetStr = "CompilationTarget::StandaloneFunctionInNonSyntacticScope"; 5294 break; 5295 case CompilationTarget::Eval: 5296 targetStr = "CompilationTarget::Eval"; 5297 break; 5298 case CompilationTarget::Module: 5299 targetStr = "CompilationTarget::Module"; 5300 break; 5301 case CompilationTarget::Delazification: 5302 targetStr = "CompilationTarget::Delazification"; 5303 break; 5304 } 5305 json.property("target", targetStr); 5306 5307 json.beginObjectProperty("options"); 5308 DumpOptionsFields(json, options); 5309 json.endObject(); 5310 5311 if (lazy_.isNull()) { 5312 json.nullProperty("lazy_"); 5313 } else { 5314 json.beginObjectProperty("lazy_"); 5315 DumpInputScriptFields(json, lazy_); 5316 json.endObject(); 5317 } 5318 5319 if (enclosingScope.isNull()) { 5320 json.nullProperty("enclosingScope"); 5321 } else { 5322 json.beginObjectProperty("enclosingScope"); 5323 DumpInputScopeFields(json, enclosingScope); 5324 json.endObject(); 5325 } 5326 5327 // TODO: Support printing the atomCache and the source fields. 5328 } 5329 5330 void CompilationStencil::dump() const { 5331 js::Fprinter out(stderr); 5332 js::JSONPrinter json(out); 5333 dump(json); 5334 out.put("\n"); 5335 } 5336 5337 void CompilationStencil::dump(js::JSONPrinter& json) const { 5338 json.beginObject(); 5339 dumpFields(json); 5340 json.endObject(); 5341 } 5342 5343 void CompilationStencil::dumpFields(js::JSONPrinter& json) const { 5344 char index[64]; 5345 5346 json.beginObjectProperty("scriptData"); 5347 for (size_t i = 0; i < scriptData.size(); i++) { 5348 SprintfLiteral(index, "ScriptIndex(%zu)", i); 5349 json.beginObjectProperty(index); 5350 scriptData[i].dumpFields(json, this); 5351 json.endObject(); 5352 } 5353 json.endObject(); 5354 5355 json.beginObjectProperty("scriptExtra"); 5356 for (size_t i = 0; i < scriptExtra.size(); i++) { 5357 SprintfLiteral(index, "ScriptIndex(%zu)", i); 5358 json.beginObjectProperty(index); 5359 scriptExtra[i].dumpFields(json); 5360 json.endObject(); 5361 } 5362 json.endObject(); 5363 5364 json.beginObjectProperty("scopeData"); 5365 MOZ_ASSERT(scopeData.size() == scopeNames.size()); 5366 for (size_t i = 0; i < scopeData.size(); i++) { 5367 SprintfLiteral(index, "ScopeIndex(%zu)", i); 5368 json.beginObjectProperty(index); 5369 scopeData[i].dumpFields(json, scopeNames[i], this); 5370 json.endObject(); 5371 } 5372 json.endObject(); 5373 5374 json.beginObjectProperty("sharedData"); 5375 sharedData.dumpFields(json); 5376 json.endObject(); 5377 5378 json.beginObjectProperty("regExpData"); 5379 for (size_t i = 0; i < regExpData.size(); i++) { 5380 SprintfLiteral(index, "RegExpIndex(%zu)", i); 5381 json.beginObjectProperty(index); 5382 regExpData[i].dumpFields(json, this); 5383 json.endObject(); 5384 } 5385 json.endObject(); 5386 5387 json.beginObjectProperty("bigIntData"); 5388 for (size_t i = 0; i < bigIntData.size(); i++) { 5389 SprintfLiteral(index, "BigIntIndex(%zu)", i); 5390 GenericPrinter& out = json.beginStringProperty(index); 5391 bigIntData[i].dumpCharsNoQuote(out); 5392 json.endStringProperty(); 5393 } 5394 json.endObject(); 5395 5396 json.beginObjectProperty("objLiteralData"); 5397 for (size_t i = 0; i < objLiteralData.size(); i++) { 5398 SprintfLiteral(index, "ObjLiteralIndex(%zu)", i); 5399 json.beginObjectProperty(index); 5400 objLiteralData[i].dumpFields(json, this); 5401 json.endObject(); 5402 } 5403 json.endObject(); 5404 5405 if (moduleMetadata) { 5406 json.beginObjectProperty("moduleMetadata"); 5407 moduleMetadata->dumpFields(json, this); 5408 json.endObject(); 5409 } 5410 5411 json.beginObjectProperty("asmJS"); 5412 if (asmJS) { 5413 for (auto iter = asmJS->moduleMap.iter(); !iter.done(); iter.next()) { 5414 SprintfLiteral(index, "ScriptIndex(%u)", iter.get().key().index); 5415 json.formatProperty(index, "asm.js"); 5416 } 5417 } 5418 json.endObject(); 5419 } 5420 5421 void CompilationStencil::dumpAtom(TaggedParserAtomIndex index) const { 5422 js::Fprinter out(stderr); 5423 js::JSONPrinter json(out); 5424 json.beginObject(); 5425 DumpTaggedParserAtomIndex(json, index, this); 5426 json.endObject(); 5427 } 5428 5429 void ExtensibleCompilationStencil::dump() { 5430 frontend::BorrowingCompilationStencil borrowingStencil(*this); 5431 borrowingStencil.dump(); 5432 } 5433 5434 void ExtensibleCompilationStencil::dump(js::JSONPrinter& json) { 5435 frontend::BorrowingCompilationStencil borrowingStencil(*this); 5436 borrowingStencil.dump(json); 5437 } 5438 5439 void ExtensibleCompilationStencil::dumpFields(js::JSONPrinter& json) { 5440 frontend::BorrowingCompilationStencil borrowingStencil(*this); 5441 borrowingStencil.dumpFields(json); 5442 } 5443 5444 void ExtensibleCompilationStencil::dumpAtom(TaggedParserAtomIndex index) { 5445 frontend::BorrowingCompilationStencil borrowingStencil(*this); 5446 borrowingStencil.dumpAtom(index); 5447 } 5448 5449 void InitialStencilAndDelazifications::dump() const { 5450 js::Fprinter out(stderr); 5451 js::JSONPrinter json(out); 5452 dump(json); 5453 out.put("\n"); 5454 } 5455 5456 void InitialStencilAndDelazifications::dump(js::JSONPrinter& json) const { 5457 json.beginObject(); 5458 dumpFields(json); 5459 json.endObject(); 5460 } 5461 5462 void InitialStencilAndDelazifications::dumpFields(js::JSONPrinter& json) const { 5463 if (initial_) { 5464 json.beginObjectProperty("initial_"); 5465 initial_->dumpFields(json); 5466 json.endObject(); 5467 } else { 5468 json.nullProperty("initial_"); 5469 } 5470 5471 for (size_t i = 0; i < delazifications_.length(); i++) { 5472 const CompilationStencil* delazification = delazifications_[i]; 5473 5474 char index[64]; 5475 SprintfLiteral(index, "ScriptIndex(%zu)", i + 1); 5476 5477 if (delazification) { 5478 json.beginObjectProperty(index); 5479 delazification->dumpFields(json); 5480 json.endObject(); 5481 } else { 5482 json.nullProperty(index); 5483 } 5484 } 5485 } 5486 5487 #endif // defined(DEBUG) || defined(JS_JITSPEW) 5488 5489 JSString* CompilationAtomCache::getExistingStringAt( 5490 ParserAtomIndex index) const { 5491 MOZ_RELEASE_ASSERT(atoms_.length() >= index); 5492 return atoms_[index]; 5493 } 5494 5495 JSString* CompilationAtomCache::getExistingStringAt( 5496 JSContext* cx, TaggedParserAtomIndex taggedIndex) const { 5497 if (taggedIndex.isParserAtomIndex()) { 5498 auto index = taggedIndex.toParserAtomIndex(); 5499 return getExistingStringAt(index); 5500 } 5501 5502 if (taggedIndex.isWellKnownAtomId()) { 5503 auto index = taggedIndex.toWellKnownAtomId(); 5504 return GetWellKnownAtom(cx, index); 5505 } 5506 5507 if (taggedIndex.isLength1StaticParserString()) { 5508 auto index = taggedIndex.toLength1StaticParserString(); 5509 return cx->staticStrings().getUnit(char16_t(index)); 5510 } 5511 5512 if (taggedIndex.isLength2StaticParserString()) { 5513 auto index = taggedIndex.toLength2StaticParserString(); 5514 return cx->staticStrings().getLength2FromIndex(size_t(index)); 5515 } 5516 5517 MOZ_ASSERT(taggedIndex.isLength3StaticParserString()); 5518 auto index = taggedIndex.toLength3StaticParserString(); 5519 return cx->staticStrings().getUint(uint32_t(index)); 5520 } 5521 5522 JSString* CompilationAtomCache::getStringAt(ParserAtomIndex index) const { 5523 if (size_t(index) >= atoms_.length()) { 5524 return nullptr; 5525 } 5526 return atoms_[index]; 5527 } 5528 5529 JSAtom* CompilationAtomCache::getExistingAtomAt(ParserAtomIndex index) const { 5530 return &getExistingStringAt(index)->asAtom(); 5531 } 5532 5533 JSAtom* CompilationAtomCache::getExistingAtomAt( 5534 JSContext* cx, TaggedParserAtomIndex taggedIndex) const { 5535 return &getExistingStringAt(cx, taggedIndex)->asAtom(); 5536 } 5537 5538 JSAtom* CompilationAtomCache::getAtomAt(ParserAtomIndex index) const { 5539 if (size_t(index) >= atoms_.length()) { 5540 return nullptr; 5541 } 5542 if (!atoms_[index]) { 5543 return nullptr; 5544 } 5545 return &atoms_[index]->asAtom(); 5546 } 5547 5548 bool CompilationAtomCache::hasAtomAt(ParserAtomIndex index) const { 5549 if (size_t(index) >= atoms_.length()) { 5550 return false; 5551 } 5552 return !!atoms_[index]; 5553 } 5554 5555 bool CompilationAtomCache::setAtomAt(FrontendContext* fc, ParserAtomIndex index, 5556 JSString* atom) { 5557 if (size_t(index) < atoms_.length()) { 5558 atoms_[index] = atom; 5559 return true; 5560 } 5561 5562 if (!atoms_.resize(size_t(index) + 1)) { 5563 ReportOutOfMemory(fc); 5564 return false; 5565 } 5566 5567 atoms_[index] = atom; 5568 return true; 5569 } 5570 5571 bool CompilationAtomCache::allocate(FrontendContext* fc, size_t length) { 5572 MOZ_ASSERT(length >= atoms_.length()); 5573 if (length == atoms_.length()) { 5574 return true; 5575 } 5576 5577 if (!atoms_.resize(length)) { 5578 ReportOutOfMemory(fc); 5579 return false; 5580 } 5581 5582 return true; 5583 } 5584 5585 void CompilationAtomCache::stealBuffer(AtomCacheVector& atoms) { 5586 atoms_ = std::move(atoms); 5587 // Destroy elements, without unreserving. 5588 atoms_.clear(); 5589 } 5590 5591 void CompilationAtomCache::releaseBuffer(AtomCacheVector& atoms) { 5592 atoms = std::move(atoms_); 5593 } 5594 5595 bool CompilationState::allocateGCThingsUninitialized( 5596 FrontendContext* fc, ScriptIndex scriptIndex, size_t length, 5597 TaggedScriptThingIndex** cursor) { 5598 MOZ_ASSERT(gcThingData.length() <= UINT32_MAX); 5599 5600 auto gcThingsOffset = CompilationGCThingIndex(gcThingData.length()); 5601 5602 if (length > INDEX_LIMIT) { 5603 ReportAllocationOverflow(fc); 5604 return false; 5605 } 5606 uint32_t gcThingsLength = length; 5607 5608 if (!gcThingData.growByUninitialized(length)) { 5609 js::ReportOutOfMemory(fc); 5610 return false; 5611 } 5612 5613 if (gcThingData.length() > UINT32_MAX) { 5614 ReportAllocationOverflow(fc); 5615 return false; 5616 } 5617 5618 ScriptStencil& script = scriptData[scriptIndex]; 5619 script.gcThingsOffset = gcThingsOffset; 5620 script.gcThingsLength = gcThingsLength; 5621 5622 *cursor = gcThingData.begin() + gcThingsOffset; 5623 return true; 5624 } 5625 5626 bool CompilationState::appendScriptStencilAndData(FrontendContext* fc) { 5627 if (!scriptData.emplaceBack()) { 5628 js::ReportOutOfMemory(fc); 5629 return false; 5630 } 5631 5632 if (isInitialStencil()) { 5633 if (!scriptExtra.emplaceBack()) { 5634 scriptData.popBack(); 5635 MOZ_ASSERT(scriptData.length() == scriptExtra.length()); 5636 5637 js::ReportOutOfMemory(fc); 5638 return false; 5639 } 5640 } 5641 5642 return true; 5643 } 5644 5645 bool CompilationState::appendGCThings( 5646 FrontendContext* fc, ScriptIndex scriptIndex, 5647 mozilla::Span<const TaggedScriptThingIndex> things) { 5648 MOZ_ASSERT(gcThingData.length() <= UINT32_MAX); 5649 5650 auto gcThingsOffset = CompilationGCThingIndex(gcThingData.length()); 5651 5652 if (things.size() > INDEX_LIMIT) { 5653 ReportAllocationOverflow(fc); 5654 return false; 5655 } 5656 uint32_t gcThingsLength = uint32_t(things.size()); 5657 5658 if (!gcThingData.append(things.data(), things.size())) { 5659 js::ReportOutOfMemory(fc); 5660 return false; 5661 } 5662 5663 if (gcThingData.length() > UINT32_MAX) { 5664 ReportAllocationOverflow(fc); 5665 return false; 5666 } 5667 5668 ScriptStencil& script = scriptData[scriptIndex]; 5669 script.gcThingsOffset = gcThingsOffset; 5670 script.gcThingsLength = gcThingsLength; 5671 return true; 5672 } 5673 5674 CompilationState::CompilationStatePosition CompilationState::getPosition() { 5675 return CompilationStatePosition{scriptData.length(), 5676 asmJS ? asmJS->moduleMap.count() : 0}; 5677 } 5678 5679 void CompilationState::rewind( 5680 const CompilationState::CompilationStatePosition& pos) { 5681 if (asmJS && asmJS->moduleMap.count() != pos.asmJSCount) { 5682 for (size_t i = pos.scriptDataLength; i < scriptData.length(); i++) { 5683 asmJS->moduleMap.remove(ScriptIndex(i)); 5684 } 5685 MOZ_ASSERT(asmJS->moduleMap.count() == pos.asmJSCount); 5686 } 5687 // scriptExtra is empty for delazification. 5688 if (scriptExtra.length()) { 5689 MOZ_ASSERT(scriptExtra.length() == scriptData.length()); 5690 scriptExtra.shrinkTo(pos.scriptDataLength); 5691 } 5692 scriptData.shrinkTo(pos.scriptDataLength); 5693 } 5694 5695 void CompilationState::markGhost( 5696 const CompilationState::CompilationStatePosition& pos) { 5697 for (size_t i = pos.scriptDataLength; i < scriptData.length(); i++) { 5698 scriptData[i].setIsGhost(); 5699 } 5700 } 5701 5702 ScriptIndex CompilationStencilMerger::getInitialScriptIndexFor( 5703 const CompilationStencil& delazification) const { 5704 auto maybeIndex = 5705 functionKeyToInitialScriptIndex_.get(delazification.functionKey); 5706 MOZ_ASSERT(maybeIndex); 5707 return *maybeIndex; 5708 } 5709 5710 template <typename T> 5711 bool FunctionKeyToScriptIndexMap::init(FrontendContext* fc, 5712 const T& scriptExtra, 5713 size_t scriptExtraSize) { 5714 if (!map_.reserve(scriptExtraSize - 1)) { 5715 ReportOutOfMemory(fc); 5716 return false; 5717 } 5718 5719 for (size_t i = 1; i < scriptExtraSize; i++) { 5720 const auto& extra = scriptExtra[i]; 5721 auto key = extra.extent.toFunctionKey(); 5722 5723 // There can be multiple ScriptStencilExtra with same extent if 5724 // the function is parsed multiple times because of rewind for 5725 // arrow function, and in that case the last one's index should be used. 5726 // Overwrite with the last one. 5727 // 5728 // Already reserved above, but OOMTest can hit failure mode in 5729 // HashTable::add. 5730 if (!map_.put(key, ScriptIndex(i))) { 5731 ReportOutOfMemory(fc); 5732 return false; 5733 } 5734 } 5735 5736 return true; 5737 } 5738 5739 bool FunctionKeyToScriptIndexMap::init(FrontendContext* fc, 5740 const CompilationStencil* initial) { 5741 return init(fc, initial->scriptExtra, initial->scriptExtra.size()); 5742 } 5743 5744 bool FunctionKeyToScriptIndexMap::init( 5745 FrontendContext* fc, const ExtensibleCompilationStencil* initial) { 5746 return init(fc, initial->scriptExtra, initial->scriptExtra.length()); 5747 } 5748 5749 mozilla::Maybe<ScriptIndex> FunctionKeyToScriptIndexMap::get( 5750 FunctionKey key) const { 5751 auto p = map_.readonlyThreadsafeLookup(key); 5752 if (!p) { 5753 return mozilla::Nothing(); 5754 } 5755 return mozilla::Some(p->value()); 5756 } 5757 5758 size_t FunctionKeyToScriptIndexMap::sizeOfExcludingThis( 5759 mozilla::MallocSizeOf mallocSizeOf) const { 5760 return map_.shallowSizeOfExcludingThis(mallocSizeOf); 5761 } 5762 5763 bool CompilationStencilMerger::buildAtomIndexMap( 5764 FrontendContext* fc, const CompilationStencil& delazification, 5765 AtomIndexMap& atomIndexMap) { 5766 uint32_t atomCount = delazification.parserAtomData.size(); 5767 if (!atomIndexMap.reserve(atomCount)) { 5768 ReportOutOfMemory(fc); 5769 return false; 5770 } 5771 for (const auto& atom : delazification.parserAtomData) { 5772 auto mappedIndex = initial_->parserAtoms.internExternalParserAtom(fc, atom); 5773 if (!mappedIndex) { 5774 return false; 5775 } 5776 atomIndexMap.infallibleAppend(mappedIndex); 5777 } 5778 return true; 5779 } 5780 5781 bool CompilationStencilMerger::setInitial( 5782 FrontendContext* fc, UniquePtr<ExtensibleCompilationStencil>&& initial) { 5783 MOZ_ASSERT(!initial_); 5784 5785 initial_ = std::move(initial); 5786 5787 return functionKeyToInitialScriptIndex_.init(fc, initial_.get()); 5788 } 5789 5790 template <typename GCThingIndexMapFunc, typename AtomIndexMapFunc, 5791 typename ScopeIndexMapFunc> 5792 static void MergeScriptStencil(ScriptStencil& dest, const ScriptStencil& src, 5793 GCThingIndexMapFunc mapGCThingIndex, 5794 AtomIndexMapFunc mapAtomIndex, 5795 ScopeIndexMapFunc mapScopeIndex, 5796 bool isTopLevel) { 5797 // If this function was lazy, all inner functions should have been lazy. 5798 MOZ_ASSERT(!dest.hasSharedData()); 5799 5800 // If the inner lazy function is skipped, gcThingsLength is empty. 5801 if (src.gcThingsLength) { 5802 dest.gcThingsOffset = mapGCThingIndex(src.gcThingsOffset); 5803 dest.gcThingsLength = src.gcThingsLength; 5804 } 5805 5806 if (src.functionAtom) { 5807 dest.functionAtom = mapAtomIndex(src.functionAtom); 5808 } 5809 5810 if (!dest.hasLazyFunctionEnclosingScopeIndex() && 5811 src.hasLazyFunctionEnclosingScopeIndex()) { 5812 // Both enclosing function and this function were lazy, and 5813 // now enclosing function is non-lazy and this function is still lazy. 5814 dest.setLazyFunctionEnclosingScopeIndex( 5815 mapScopeIndex(src.lazyFunctionEnclosingScopeIndex())); 5816 } else if (dest.hasLazyFunctionEnclosingScopeIndex() && 5817 !src.hasLazyFunctionEnclosingScopeIndex()) { 5818 // The enclosing function was non-lazy and this function was lazy, and 5819 // now this function is non-lazy. 5820 dest.resetHasLazyFunctionEnclosingScopeIndexAfterStencilMerge(); 5821 } else { 5822 // The enclosing function is still lazy. 5823 MOZ_ASSERT(!dest.hasLazyFunctionEnclosingScopeIndex()); 5824 MOZ_ASSERT(!src.hasLazyFunctionEnclosingScopeIndex()); 5825 } 5826 5827 #ifdef DEBUG 5828 uint16_t BASESCRIPT = uint16_t(FunctionFlags::Flags::BASESCRIPT); 5829 uint16_t HAS_INFERRED_NAME = 5830 uint16_t(FunctionFlags::Flags::HAS_INFERRED_NAME); 5831 uint16_t HAS_GUESSED_ATOM = uint16_t(FunctionFlags::Flags::HAS_GUESSED_ATOM); 5832 uint16_t acceptableDifferenceForLazy = HAS_INFERRED_NAME | HAS_GUESSED_ATOM; 5833 uint16_t acceptableDifferenceForNonLazy = 5834 BASESCRIPT | HAS_INFERRED_NAME | HAS_GUESSED_ATOM; 5835 5836 MOZ_ASSERT_IF( 5837 isTopLevel, 5838 (dest.functionFlags.toRaw() | acceptableDifferenceForNonLazy) == 5839 (src.functionFlags.toRaw() | acceptableDifferenceForNonLazy)); 5840 5841 // NOTE: Currently we don't delazify inner functions. 5842 MOZ_ASSERT_IF(!isTopLevel, 5843 (dest.functionFlags.toRaw() | acceptableDifferenceForLazy) == 5844 (src.functionFlags.toRaw() | acceptableDifferenceForLazy)); 5845 #endif // DEBUG 5846 dest.functionFlags = src.functionFlags; 5847 5848 // Other flags. 5849 5850 if (src.wasEmittedByEnclosingScript()) { 5851 // NOTE: the top-level function of the delazification have 5852 // src.wasEmittedByEnclosingScript() == false, and that shouldn't 5853 // be copied. 5854 dest.setWasEmittedByEnclosingScript(); 5855 } 5856 5857 if (src.allowRelazify()) { 5858 dest.setAllowRelazify(); 5859 } 5860 5861 if (src.hasSharedData()) { 5862 dest.setHasSharedData(); 5863 } 5864 } 5865 5866 bool CompilationStencilMerger::addDelazification( 5867 FrontendContext* fc, const CompilationStencil& delazification) { 5868 MOZ_ASSERT(initial_); 5869 5870 auto delazifiedFunctionIndex = getInitialScriptIndexFor(delazification); 5871 auto& destFun = initial_->scriptData[delazifiedFunctionIndex]; 5872 5873 if (destFun.hasSharedData()) { 5874 // If the function was already non-lazy, it means the following happened: 5875 // A. delazified twice within single collecting delazifications 5876 // 1. this function is lazily parsed 5877 // 2. collecting delazifications is started 5878 // 3. this function is delazified, encoded, and merged 5879 // 4. this function is relazified 5880 // 5. this function is delazified, encoded, and merged 5881 // 5882 // B. delazified twice across decode 5883 // 1. this function is lazily parsed 5884 // 2. collecting delazifications is started 5885 // 3. this function is delazified, encoded, and merged 5886 // 4. collecting delazifications is finished 5887 // 5. decoded 5888 // 6. collecting delazifications is started 5889 // here, this function is non-lazy 5890 // 7. this function is relazified 5891 // 8. this function is delazified, encoded, and merged 5892 // 5893 // A can happen with public API. 5894 // 5895 // B cannot happen with public API, but can happen if incremental 5896 // encoding at step B.6 is explicitly started by internal function. 5897 // See Evaluate and StartCollectingDelazifications in js/src/shell/js.cpp. 5898 return true; 5899 } 5900 5901 // If any failure happens, the initial stencil is left in the broken state. 5902 // Immediately discard it. 5903 auto failureCase = mozilla::MakeScopeExit([&] { initial_.reset(); }); 5904 5905 mozilla::Maybe<ScopeIndex> functionEnclosingScope; 5906 if (destFun.hasLazyFunctionEnclosingScopeIndex()) { 5907 // lazyFunctionEnclosingScopeIndex_ can be Nothing if this is 5908 // top-level function. 5909 functionEnclosingScope = 5910 mozilla::Some(destFun.lazyFunctionEnclosingScopeIndex()); 5911 } 5912 5913 // A map from ParserAtomIndex in delazification to TaggedParserAtomIndex 5914 // in initial_. 5915 AtomIndexMap atomIndexMap; 5916 if (!buildAtomIndexMap(fc, delazification, atomIndexMap)) { 5917 return false; 5918 } 5919 auto mapAtomIndex = [&](TaggedParserAtomIndex index) { 5920 if (index.isParserAtomIndex()) { 5921 return atomIndexMap[index.toParserAtomIndex()]; 5922 } 5923 5924 return index; 5925 }; 5926 5927 size_t gcThingOffset = initial_->gcThingData.length(); 5928 size_t regExpOffset = initial_->regExpData.length(); 5929 size_t bigIntOffset = initial_->bigIntData.length(); 5930 size_t objLiteralOffset = initial_->objLiteralData.length(); 5931 size_t scopeOffset = initial_->scopeData.length(); 5932 5933 // Map delazification's ScriptIndex to initial's ScriptIndex. 5934 // 5935 // The lazy function's gcthings list stores inner function's ScriptIndex. 5936 // The n-th gcthing holds the ScriptIndex of the (n+1)-th script in 5937 // delazification. 5938 // 5939 // NOTE: Currently we don't delazify inner functions. 5940 auto lazyFunctionGCThingsOffset = destFun.gcThingsOffset; 5941 auto mapScriptIndex = [&](ScriptIndex index) { 5942 if (index == CompilationStencil::TopLevelIndex) { 5943 return delazifiedFunctionIndex; 5944 } 5945 5946 return initial_->gcThingData[lazyFunctionGCThingsOffset + index.index - 1] 5947 .toFunction(); 5948 }; 5949 5950 // Map other delazification's indices into initial's indices. 5951 auto mapGCThingIndex = [&](CompilationGCThingIndex offset) { 5952 return CompilationGCThingIndex(gcThingOffset + offset.index); 5953 }; 5954 auto mapRegExpIndex = [&](RegExpIndex index) { 5955 return RegExpIndex(regExpOffset + index.index); 5956 }; 5957 auto mapBigIntIndex = [&](BigIntIndex index) { 5958 return BigIntIndex(bigIntOffset + index.index); 5959 }; 5960 auto mapObjLiteralIndex = [&](ObjLiteralIndex index) { 5961 return ObjLiteralIndex(objLiteralOffset + index.index); 5962 }; 5963 auto mapScopeIndex = [&](ScopeIndex index) { 5964 return ScopeIndex(scopeOffset + index.index); 5965 }; 5966 5967 // Append gcThingData, with mapping TaggedScriptThingIndex. 5968 if (!initial_->gcThingData.append(delazification.gcThingData.data(), 5969 delazification.gcThingData.size())) { 5970 js::ReportOutOfMemory(fc); 5971 return false; 5972 } 5973 for (size_t i = gcThingOffset; i < initial_->gcThingData.length(); i++) { 5974 auto& index = initial_->gcThingData[i]; 5975 if (index.isNull()) { 5976 // Nothing to do. 5977 } else if (index.isAtom()) { 5978 index = TaggedScriptThingIndex(mapAtomIndex(index.toAtom())); 5979 } else if (index.isBigInt()) { 5980 index = TaggedScriptThingIndex(mapBigIntIndex(index.toBigInt())); 5981 } else if (index.isObjLiteral()) { 5982 index = TaggedScriptThingIndex(mapObjLiteralIndex(index.toObjLiteral())); 5983 } else if (index.isRegExp()) { 5984 index = TaggedScriptThingIndex(mapRegExpIndex(index.toRegExp())); 5985 } else if (index.isScope()) { 5986 index = TaggedScriptThingIndex(mapScopeIndex(index.toScope())); 5987 } else if (index.isFunction()) { 5988 index = TaggedScriptThingIndex(mapScriptIndex(index.toFunction())); 5989 } else { 5990 MOZ_ASSERT(index.isEmptyGlobalScope()); 5991 // Nothing to do 5992 } 5993 } 5994 5995 // Append regExpData, with mapping RegExpStencil.atom_. 5996 if (!initial_->regExpData.append(delazification.regExpData.data(), 5997 delazification.regExpData.size())) { 5998 js::ReportOutOfMemory(fc); 5999 return false; 6000 } 6001 for (size_t i = regExpOffset; i < initial_->regExpData.length(); i++) { 6002 auto& data = initial_->regExpData[i]; 6003 data.atom_ = mapAtomIndex(data.atom_); 6004 } 6005 6006 // Append bigIntData, with copying BigIntStencil.bigInt_. 6007 if (!initial_->bigIntData.reserve(bigIntOffset + 6008 delazification.bigIntData.size())) { 6009 js::ReportOutOfMemory(fc); 6010 return false; 6011 } 6012 for (const auto& data : delazification.bigIntData) { 6013 initial_->bigIntData.infallibleEmplaceBack(); 6014 if (!initial_->bigIntData.back().init(fc, initial_->alloc, data)) { 6015 return false; 6016 } 6017 } 6018 6019 // Append objLiteralData, with copying ObjLiteralStencil.code_, and mapping 6020 // TaggedParserAtomIndex in it. 6021 if (!initial_->objLiteralData.reserve(objLiteralOffset + 6022 delazification.objLiteralData.size())) { 6023 js::ReportOutOfMemory(fc); 6024 return false; 6025 } 6026 for (const auto& data : delazification.objLiteralData) { 6027 size_t length = data.code().size(); 6028 auto* code = initial_->alloc.newArrayUninitialized<uint8_t>(length); 6029 if (!code) { 6030 js::ReportOutOfMemory(fc); 6031 return false; 6032 } 6033 memcpy(code, data.code().data(), length); 6034 6035 ObjLiteralModifier modifier(mozilla::Span(code, length)); 6036 modifier.mapAtom(mapAtomIndex); 6037 6038 initial_->objLiteralData.infallibleEmplaceBack( 6039 code, length, data.kind(), data.flags(), data.propertyCount()); 6040 } 6041 6042 // Append scopeData, with mapping indices in ScopeStencil fields. 6043 // And append scopeNames, with copying the entire data, and mapping 6044 // trailingNames. 6045 if (!initial_->scopeData.reserve(scopeOffset + 6046 delazification.scopeData.size())) { 6047 js::ReportOutOfMemory(fc); 6048 return false; 6049 } 6050 if (!initial_->scopeNames.reserve(scopeOffset + 6051 delazification.scopeNames.size())) { 6052 js::ReportOutOfMemory(fc); 6053 return false; 6054 } 6055 for (size_t i = 0; i < delazification.scopeData.size(); i++) { 6056 const auto& srcData = delazification.scopeData[i]; 6057 const auto* srcNames = delazification.scopeNames[i]; 6058 6059 mozilla::Maybe<ScriptIndex> functionIndex = mozilla::Nothing(); 6060 if (srcData.isFunction()) { 6061 // Inner functions should be in the same order as initial, beginning from 6062 // the delazification's index. 6063 functionIndex = mozilla::Some(mapScriptIndex(srcData.functionIndex())); 6064 } 6065 6066 BaseParserScopeData* destNames = nullptr; 6067 if (srcNames) { 6068 destNames = CopyScopeData(fc, initial_->alloc, srcData.kind(), srcNames); 6069 if (!destNames) { 6070 return false; 6071 } 6072 auto trailingNames = 6073 GetParserScopeDataTrailingNames(srcData.kind(), destNames); 6074 for (auto& name : trailingNames) { 6075 if (name.name()) { 6076 name.updateNameAfterStencilMerge(mapAtomIndex(name.name())); 6077 } 6078 } 6079 } 6080 6081 initial_->scopeData.infallibleEmplaceBack( 6082 srcData.kind(), 6083 srcData.hasEnclosing() 6084 ? mozilla::Some(mapScopeIndex(srcData.enclosing())) 6085 : functionEnclosingScope, 6086 srcData.firstFrameSlot(), 6087 srcData.hasEnvironmentShape() 6088 ? mozilla::Some(srcData.numEnvironmentSlots()) 6089 : mozilla::Nothing(), 6090 functionIndex, srcData.isArrow()); 6091 6092 initial_->scopeNames.infallibleEmplaceBack(destNames); 6093 } 6094 6095 // Add delazified function's shared data. 6096 // 6097 // NOTE: Currently we don't delazify inner functions. 6098 if (!initial_->sharedData.addExtraWithoutShare( 6099 fc, delazifiedFunctionIndex, 6100 delazification.sharedData.get(CompilationStencil::TopLevelIndex))) { 6101 return false; 6102 } 6103 6104 // Update scriptData, with mapping indices in ScriptStencil fields. 6105 for (uint32_t i = 0; i < delazification.scriptData.size(); i++) { 6106 auto destIndex = mapScriptIndex(ScriptIndex(i)); 6107 MergeScriptStencil(initial_->scriptData[destIndex], 6108 delazification.scriptData[i], mapGCThingIndex, 6109 mapAtomIndex, mapScopeIndex, 6110 i == CompilationStencil::TopLevelIndex); 6111 } 6112 6113 // WARNING: moduleMetadata and asmJS fields are known at script/module 6114 // top-level parsing, any mutation made in this function should be reflected 6115 // to ExtensibleCompilationStencil::steal and CompilationStencil::clone. 6116 6117 // Function shouldn't be a module. 6118 MOZ_ASSERT(!delazification.moduleMetadata); 6119 6120 // asm.js shouldn't appear inside delazification, given asm.js forces 6121 // full-parse. 6122 MOZ_ASSERT(!delazification.hasAsmJS()); 6123 6124 failureCase.release(); 6125 return true; 6126 } 6127 6128 bool CompilationStencilMerger::maybeAddDelazification( 6129 FrontendContext* fc, const CompilationStencil& delazification) { 6130 auto delazifiedFunctionIndex = getInitialScriptIndexFor(delazification); 6131 auto& destFun = initial_->scriptData[delazifiedFunctionIndex]; 6132 6133 if (!destFun.hasLazyFunctionEnclosingScopeIndex()) { 6134 // The enclosing function is still lazy, and this inner function cannot 6135 // be added. 6136 return true; 6137 } 6138 6139 return addDelazification(fc, delazification); 6140 } 6141 6142 void CompilationStencil::AddRef() { refCount_++; } 6143 void CompilationStencil::Release() { 6144 MOZ_RELEASE_ASSERT(refCount_ > 0); 6145 if (--refCount_ == 0) { 6146 js_delete(this); 6147 } 6148 } 6149 6150 void JS::StencilAddRef(JS::Stencil* stencil) { stencil->AddRef(); } 6151 void JS::StencilRelease(JS::Stencil* stencil) { stencil->Release(); } 6152 6153 JS_PUBLIC_API JSScript* JS::InstantiateGlobalStencil( 6154 JSContext* cx, const JS::InstantiateOptions& options, JS::Stencil* stencil, 6155 JS::InstantiationStorage* storage) { 6156 MOZ_ASSERT_IF(storage, storage->isValid()); 6157 6158 CompileOptions compileOptions(cx); 6159 options.copyTo(compileOptions); 6160 Rooted<CompilationInput> input(cx, CompilationInput(compileOptions)); 6161 Rooted<CompilationGCOutput> gcOutput(cx); 6162 if (storage) { 6163 gcOutput.get().steal(std::move(*storage->gcOutput_)); 6164 } 6165 6166 if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) { 6167 return nullptr; 6168 } 6169 return gcOutput.get().script; 6170 } 6171 6172 JS_PUBLIC_API bool JS::StencilIsBorrowed(JS::Stencil* stencil) { 6173 return stencil->getInitial()->storageType == 6174 CompilationStencil::StorageType::Borrowed; 6175 } 6176 6177 JS_PUBLIC_API JSObject* JS::InstantiateModuleStencil( 6178 JSContext* cx, const JS::InstantiateOptions& options, JS::Stencil* stencil, 6179 JS::InstantiationStorage* storage) { 6180 MOZ_ASSERT_IF(storage, storage->isValid()); 6181 6182 CompileOptions compileOptions(cx); 6183 options.copyTo(compileOptions); 6184 compileOptions.setModule(); 6185 Rooted<CompilationInput> input(cx, CompilationInput(compileOptions)); 6186 Rooted<CompilationGCOutput> gcOutput(cx); 6187 if (storage) { 6188 gcOutput.get().steal(std::move(*storage->gcOutput_)); 6189 } 6190 6191 if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) { 6192 return nullptr; 6193 } 6194 return gcOutput.get().module; 6195 } 6196 6197 JS_PUBLIC_API size_t JS::SizeOfStencil(Stencil* stencil, 6198 mozilla::MallocSizeOf mallocSizeOf) { 6199 return stencil->sizeOfIncludingThis(mallocSizeOf); 6200 } 6201 6202 JS::InstantiationStorage::~InstantiationStorage() { 6203 if (gcOutput_) { 6204 js_delete(gcOutput_); 6205 gcOutput_ = nullptr; 6206 } 6207 } 6208 6209 bool JS::IsStencilCacheable(JS::Stencil* stencil) { 6210 if (stencil->hasAsmJS()) { 6211 return false; 6212 } 6213 6214 return true; 6215 } 6216 6217 JS_PUBLIC_API size_t JS::GetScriptSourceLength(JS::Stencil* stencil) { 6218 const ScriptSource* source = stencil->getInitial()->source; 6219 if (!source->hasSourceText()) { 6220 return 0; 6221 } 6222 return source->length(); 6223 }