CompilationStencil.h (99032B)
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 #ifndef frontend_CompilationStencil_h 8 #define frontend_CompilationStencil_h 9 10 #include "mozilla/AlreadyAddRefed.h" // already_AddRefed 11 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_RELEASE_ASSERT, MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE 12 #include "mozilla/Atomics.h" // mozilla::Atomic 13 #include "mozilla/Attributes.h" // MOZ_RAII, MOZ_STACK_CLASS 14 #include "mozilla/HashTable.h" // mozilla::HashMap, mozilla::DefaultHasher 15 #include "mozilla/Maybe.h" // mozilla::Maybe 16 #include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf 17 #include "mozilla/RefPtr.h" // RefPtr 18 #include "mozilla/Span.h" // mozilla::Span 19 #include "mozilla/Variant.h" // mozilla::Variant 20 21 #include <algorithm> // std::swap 22 #include <stddef.h> // size_t 23 #include <stdint.h> // uint32_t, uintptr_t 24 #include <type_traits> // std::is_pointer_v 25 #include <utility> // std::forward, std::move 26 27 #include "ds/LifoAlloc.h" // LifoAlloc, LifoAllocScope 28 #include "frontend/FrontendContext.h" // FrontendContext 29 #include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind 30 #include "frontend/NameAnalysisTypes.h" // NameLocation 31 #include "frontend/ParserAtom.h" // ParserAtomsTable, ParserAtomIndex, TaggedParserAtomIndex, ParserAtomSpan 32 #include "frontend/ScopeIndex.h" // ScopeIndex 33 #include "frontend/ScriptIndex.h" // ScriptIndex 34 #include "frontend/SharedContext.h" // ThisBinding, InheritThis, Directives 35 #include "frontend/Stencil.h" // ScriptStencil, ScriptStencilExtra, ScopeStencil, RegExpStencil, BigIntStencil, ObjLiteralStencil, BaseParserScopeData, StencilModuleMetadata 36 #include "frontend/TaggedParserAtomIndexHasher.h" // TaggedParserAtomIndexHasher 37 #include "frontend/UsedNameTracker.h" // UsedNameTracker 38 #include "js/AllocPolicy.h" // SystemAllocPolicy, ReportOutOfMemory 39 #include "js/GCVector.h" // JS::GCVector 40 #include "js/RefCounted.h" // AtomicRefCounted 41 #include "js/RootingAPI.h" // JS::Handle 42 #include "js/Transcoding.h" // JS::TranscodeBuffer, JS::TranscodeRange 43 #include "js/UniquePtr.h" // js::UniquePtr 44 #include "js/Vector.h" // Vector 45 #include "js/WasmModule.h" // JS::WasmModule 46 #include "vm/FunctionFlags.h" // FunctionFlags 47 #include "vm/GlobalObject.h" // GlobalObject 48 #include "vm/JSContext.h" // JSContext 49 #include "vm/JSFunction.h" // JSFunction 50 #include "vm/JSScript.h" // BaseScript, ScriptSource, SourceExtent 51 #include "vm/Realm.h" // JSContext::global 52 #include "vm/Scope.h" // Scope, ModuleScope 53 #include "vm/ScopeKind.h" // ScopeKind 54 #include "vm/SharedStencil.h" // ImmutableScriptFlags, MemberInitializers, SharedImmutableScriptData, RO_IMMUTABLE_SCRIPT_FLAGS 55 56 class JSAtom; 57 class JSFunction; 58 class JSObject; 59 class JSString; 60 class JSTracer; 61 62 namespace JS { 63 class JS_PUBLIC_API ReadOnlyCompileOptions; 64 } 65 66 namespace js { 67 68 class AtomSet; 69 class JSONPrinter; 70 class ModuleObject; 71 72 namespace frontend { 73 74 struct InitialStencilAndDelazifications; 75 struct CompilationInput; 76 struct CompilationStencil; 77 struct CompilationGCOutput; 78 struct PreallocatedCompilationGCOutput; 79 class ScriptStencilIterable; 80 struct InputName; 81 class ScopeBindingCache; 82 struct ScriptStencilRef; 83 84 // When delazifying modules' inner functions, the actual global scope is used. 85 // However, when doing a delazification the global scope is not available. We 86 // use this dummy type to be a placeholder to be used as part of the InputScope 87 // variants to mimic what the Global scope would be used for. 88 struct FakeStencilGlobalScope {}; 89 90 // Reference to a Scope within an InitialStencilAndDelazifications. 91 struct ScopeStencilRef { 92 const InitialStencilAndDelazifications& stencils_; 93 // Index of the script in the initial stencil of stencils_, where the script 94 // holds the scope. 95 const ScriptIndex scriptIndex_; 96 // Index of the scope in te CompilationStencil (either initial or the 97 // delazification) pointed by the stencils_ and scriptIndex_. 98 const ScopeIndex scopeIndex_; 99 100 // Lookup the ScopeStencil referenced by this ScopeStencilRef. 101 inline const ScopeStencil& scope() const; 102 // Reference to the script which owns the scope pointed by this object. 103 inline ScriptStencilRef script() const; 104 105 // For a Function scope, return the ScriptExtra information from the initial 106 // stencil. 107 inline const ScriptStencilExtra& functionScriptExtra() const; 108 109 // CompilationStencil (either initial or delazification) which contains the 110 // scope data. 111 inline const CompilationStencil* context() const; 112 }; 113 114 // Wraps a scope for a CompilationInput. The scope is either as a GC pointer to 115 // an instantiated scope, or as a reference to a CompilationStencil. 116 // 117 // Note: A scope reference may be nullptr/InvalidIndex if there is no such 118 // scope, such as the enclosingScope at the end of a scope chain. See `isNull` 119 // helper. 120 class InputScope { 121 using InputScopeStorage = 122 mozilla::Variant<Scope*, ScopeStencilRef, FakeStencilGlobalScope>; 123 InputScopeStorage scope_; 124 125 public: 126 // Create an InputScope given an instantiated scope. 127 explicit InputScope(Scope* ptr) : scope_(ptr) {} 128 129 // Create an InputScope for a global. 130 explicit InputScope(FakeStencilGlobalScope global) : scope_(global) {} 131 132 // Create an InputScope given a CompilationStencil and the ScopeIndex which is 133 // an offset within the same CompilationStencil given as argument. 134 InputScope(const InitialStencilAndDelazifications& stencils, 135 ScriptIndex scriptIndex, ScopeIndex scopeIndex) 136 : scope_(ScopeStencilRef{stencils, scriptIndex, scopeIndex}) {} 137 138 // Returns the variant used by the InputScope. This can be useful for complex 139 // cases where the following accessors are not enough. 140 const InputScopeStorage& variant() const { return scope_; } 141 InputScopeStorage& variant() { return scope_; } 142 143 // This match function will unwrap the variant for each type, and will 144 // specialize and call the Matcher given as argument with the type and value 145 // of the stored pointer / reference. 146 // 147 // This is useful for cases where the code is literally identical despite 148 // having different specializations. This is achiveable by relying on 149 // function overloading when the usage differ between the 2 types. 150 // 151 // Example: 152 // inputScope.match([](auto& scope) { 153 // // scope is either a `Scope*` or a `ScopeStencilRef`. 154 // for (auto bi = InputBindingIter(scope); bi; bi++) { 155 // InputName name(scope, bi.name()); 156 // // ... 157 // } 158 // }); 159 template <typename Matcher> 160 decltype(auto) match(Matcher&& matcher) const& { 161 return scope_.match(std::forward<Matcher>(matcher)); 162 } 163 template <typename Matcher> 164 decltype(auto) match(Matcher&& matcher) & { 165 return scope_.match(std::forward<Matcher>(matcher)); 166 } 167 168 bool isNull() const { 169 return scope_.match( 170 [](const Scope* ptr) { return !ptr; }, 171 [](const ScopeStencilRef& ref) { return !ref.scopeIndex_.isValid(); }, 172 [](const FakeStencilGlobalScope&) { return false; }); 173 } 174 175 ScopeKind kind() const { 176 return scope_.match( 177 [](const Scope* ptr) { return ptr->kind(); }, 178 [](const ScopeStencilRef& ref) { return ref.scope().kind(); }, 179 [](const FakeStencilGlobalScope&) { return ScopeKind::Global; }); 180 }; 181 bool hasEnvironment() const { 182 return scope_.match( 183 [](const Scope* ptr) { return ptr->hasEnvironment(); }, 184 [](const ScopeStencilRef& ref) { return ref.scope().hasEnvironment(); }, 185 [](const FakeStencilGlobalScope&) { 186 // See Scope::hasEnvironment 187 return true; 188 }); 189 }; 190 inline InputScope enclosing() const; 191 bool hasOnChain(ScopeKind kind) const { 192 return scope_.match([=](const Scope* ptr) { return ptr->hasOnChain(kind); }, 193 [=](const ScopeStencilRef& ref) { 194 ScopeStencilRef it = ref; 195 while (true) { 196 const ScopeStencil& scope = it.scope(); 197 if (scope.kind() == kind) { 198 return true; 199 } 200 if (scope.kind() == ScopeKind::Module && 201 kind == ScopeKind::Global) { 202 return true; 203 } 204 if (!scope.hasEnclosing()) { 205 break; 206 } 207 new (&it) 208 ScopeStencilRef{ref.stencils_, ref.scriptIndex_, 209 scope.enclosing()}; 210 } 211 return false; 212 }, 213 [=](const FakeStencilGlobalScope&) { 214 return kind == ScopeKind::Global; 215 }); 216 } 217 uint32_t environmentChainLength() const { 218 return scope_.match( 219 [](const Scope* ptr) { return ptr->environmentChainLength(); }, 220 [](const ScopeStencilRef& ref) { 221 uint32_t length = 0; 222 ScopeStencilRef it = ref; 223 while (true) { 224 const ScopeStencil& scope = it.scope(); 225 if (scope.hasEnvironment() && 226 scope.kind() != ScopeKind::NonSyntactic) { 227 length++; 228 } 229 if (scope.kind() == ScopeKind::Module) { 230 // Stencil do not encode the Global scope, as it used to be 231 // assumed to already exists. As moving delazification off-thread, 232 // we need to materialize a fake-stencil version of the Global 233 // Scope. 234 MOZ_ASSERT(!scope.hasEnclosing()); 235 length += js::ModuleScope::EnclosingEnvironmentChainLength; 236 } 237 if (!scope.hasEnclosing()) { 238 break; 239 } 240 new (&it) ScopeStencilRef{ref.stencils_, ref.scriptIndex_, 241 scope.enclosing()}; 242 } 243 return length; 244 }, 245 [=](const FakeStencilGlobalScope&) { 246 // Stencil-based delazification needs to calculate 247 // environmentChainLength where the global is not available. 248 // 249 // The FakeStencilGlobalScope is used to represent what the global 250 // would be if we had access to it while delazifying. 251 return uint32_t(js::ModuleScope::EnclosingEnvironmentChainLength); 252 }); 253 } 254 void trace(JSTracer* trc); 255 bool isStencil() const { return !scope_.is<Scope*>(); }; 256 257 // Various accessors which are valid only when the InputScope is a 258 // FunctionScope. Some of these accessors are returning values associated with 259 // the canonical function. 260 private: 261 inline FunctionFlags functionFlags() const; 262 inline ImmutableScriptFlags immutableFlags() const; 263 264 public: 265 inline MemberInitializers getMemberInitializers() const; 266 RO_IMMUTABLE_SCRIPT_FLAGS(immutableFlags()) 267 bool isArrow() const { return functionFlags().isArrow(); } 268 bool allowSuperProperty() const { 269 return functionFlags().allowSuperProperty(); 270 } 271 bool isClassConstructor() const { 272 return functionFlags().isClassConstructor(); 273 } 274 }; 275 276 // Reference to a Script within an InitialStencilAndDelazifications. 277 struct ScriptStencilRef { 278 const InitialStencilAndDelazifications& stencils_; 279 // Index of the script within the initial CompilationStencil of stencils_. 280 const ScriptIndex scriptIndex_; 281 282 // Returns a ScriptStencilRef corresponding to the top-level script, which is 283 // the first script in the initial stencil. 284 inline ScriptStencilRef topLevelScript() const; 285 286 // Returns a ScriptStencilRef which corresponds to the enclosing script of the 287 // current script. 288 inline ScriptStencilRef enclosingScript() const; 289 290 // scriptData about the current script, held by the enclosing (initial / 291 // delazification) stencil. 292 // 293 // This function is used to get function flags known by the caller, and when 294 // looking for scope index in the enclosing stencil. 295 inline const ScriptStencil& scriptDataFromEnclosing() const; 296 297 // scriptData about the current script, held by the initial stencil. 298 // 299 // This function is used to implement gcThingsFromInitial, and also query 300 // whether this script is compiled as part of the initial stencil or not. 301 inline const ScriptStencil& scriptDataFromInitial() const; 302 303 // Returns whether the script is held by the initial stencil. 304 inline bool isEagerlyCompiledInInitial() const; 305 306 // scriptExtra about the current script, held by the initial stencil. 307 inline const ScriptStencilExtra& scriptExtra() const; 308 309 // gcThings about the current script, held by the initial stencil. Unless this 310 // script is compiled as part of the top-level, it would most likely only 311 // contain the list of inner functions. 312 inline mozilla::Span<TaggedScriptThingIndex> gcThingsFromInitial() const; 313 314 // Initial or delazification stencil which holds the the compilation result 315 // for the current scriptIndex_. 316 inline const CompilationStencil* context() const; 317 inline const CompilationStencil* maybeContext() const; 318 }; 319 320 // Wraps a script for a CompilationInput. The script is either as a BaseScript 321 // pointer to an instantiated script, or as a reference to a CompilationStencil. 322 class InputScript { 323 using InputScriptStorage = mozilla::Variant<BaseScript*, ScriptStencilRef>; 324 InputScriptStorage script_; 325 326 public: 327 // Create an InputScript given an instantiated BaseScript pointer. 328 explicit InputScript(BaseScript* ptr) : script_(ptr) {} 329 330 // Create an InputScript given a CompilationStencil and the ScriptIndex which 331 // is an offset within the same CompilationStencil given as argument. 332 InputScript(const InitialStencilAndDelazifications& stencils, 333 ScriptIndex scriptIndex) 334 : script_(ScriptStencilRef{stencils, scriptIndex}) {} 335 336 const InputScriptStorage& raw() const { return script_; } 337 InputScriptStorage& raw() { return script_; } 338 339 SourceExtent extent() const { 340 return script_.match( 341 [](const BaseScript* ptr) { return ptr->extent(); }, 342 [](const ScriptStencilRef& ref) { return ref.scriptExtra().extent; }); 343 } 344 ImmutableScriptFlags immutableFlags() const { 345 return script_.match( 346 [](const BaseScript* ptr) { return ptr->immutableFlags(); }, 347 [](const ScriptStencilRef& ref) { 348 return ref.scriptExtra().immutableFlags; 349 }); 350 } 351 RO_IMMUTABLE_SCRIPT_FLAGS(immutableFlags()) 352 FunctionFlags functionFlags() const { 353 return script_.match( 354 [](const BaseScript* ptr) { return ptr->function()->flags(); }, 355 [](const ScriptStencilRef& ref) { 356 auto& scriptData = ref.scriptDataFromEnclosing(); 357 return scriptData.functionFlags; 358 }); 359 } 360 bool hasPrivateScriptData() const { 361 return script_.match( 362 [](const BaseScript* ptr) { return ptr->hasPrivateScriptData(); }, 363 [](const ScriptStencilRef& ref) { 364 // See BaseScript::CreateRawLazy. 365 auto& scriptData = ref.scriptDataFromEnclosing(); 366 return scriptData.hasGCThings() || 367 ref.scriptExtra().useMemberInitializers(); 368 }); 369 } 370 InputScope enclosingScope() const { 371 return script_.match( 372 [](const BaseScript* ptr) { 373 return InputScope(ptr->function()->enclosingScope()); 374 }, 375 [](const ScriptStencilRef& ref) { 376 // The ScriptStencilRef only reference lazy Script, otherwise we 377 // should fetch the enclosing scope using the bodyScope field of the 378 // immutable data which is a reference to the vector of gc-things. 379 auto enclosing = ref.enclosingScript(); 380 auto& scriptData = ref.scriptDataFromEnclosing(); 381 MOZ_RELEASE_ASSERT(!scriptData.hasSharedData()); 382 MOZ_ASSERT(scriptData.hasLazyFunctionEnclosingScopeIndex()); 383 auto scopeIndex = scriptData.lazyFunctionEnclosingScopeIndex(); 384 return InputScope(ref.stencils_, enclosing.scriptIndex_, scopeIndex); 385 }); 386 } 387 MemberInitializers getMemberInitializers() const { 388 return script_.match( 389 [](const BaseScript* ptr) { return ptr->getMemberInitializers(); }, 390 [](const ScriptStencilRef& ref) { 391 return ref.scriptExtra().memberInitializers(); 392 }); 393 } 394 395 InputName displayAtom() const; 396 void trace(JSTracer* trc); 397 bool isNull() const { 398 return script_.match([](const BaseScript* ptr) { return !ptr; }, 399 [](const ScriptStencilRef& ref) { return false; }); 400 } 401 bool isStencil() const { 402 return script_.match([](const BaseScript* ptr) { return false; }, 403 [](const ScriptStencilRef&) { return true; }); 404 } 405 406 ScriptSourceObject* sourceObject() const { 407 return script_.match( 408 [](const BaseScript* ptr) { return ptr->sourceObject(); }, 409 [](const ScriptStencilRef&) { 410 return static_cast<ScriptSourceObject*>(nullptr); 411 }); 412 } 413 }; 414 415 // Iterator for walking the scope chain, this is identical to ScopeIter but 416 // accept an InputScope instead of a Scope pointer. 417 // 418 // It may be placed in GC containers; for example: 419 // 420 // for (Rooted<InputScopeIter> si(cx, InputScopeIter(scope)); si; si++) { 421 // use(si); 422 // SomeMayGCOperation(); 423 // use(si); 424 // } 425 // 426 class MOZ_STACK_CLASS InputScopeIter { 427 InputScope scope_; 428 429 public: 430 explicit InputScopeIter(const InputScope& scope) : scope_(scope) {} 431 432 InputScope& scope() { 433 MOZ_ASSERT(!done()); 434 return scope_; 435 } 436 437 const InputScope& scope() const { 438 MOZ_ASSERT(!done()); 439 return scope_; 440 } 441 442 bool done() const { return scope_.isNull(); } 443 explicit operator bool() const { return !done(); } 444 void operator++(int) { scope_ = scope_.enclosing(); } 445 ScopeKind kind() const { return scope_.kind(); } 446 447 // Returns whether this scope has a syntactic environment (i.e., an 448 // Environment that isn't a non-syntactic With or NonSyntacticVariables) 449 // on the environment chain. 450 bool hasSyntacticEnvironment() const { 451 return scope_.hasEnvironment() && scope_.kind() != ScopeKind::NonSyntactic; 452 } 453 454 void trace(JSTracer* trc) { scope_.trace(trc); } 455 }; 456 457 // Reference to a Binding Name within an existing CompilationStencil. 458 // TaggedParserAtomIndex are in some cases indexes in the parserAtomData of the 459 // CompilationStencil. 460 struct NameStencilRef { 461 const CompilationStencil& context_; 462 const TaggedParserAtomIndex atomIndex_; 463 }; 464 465 // Wraps a name for a CompilationInput. The name is either as a GC pointer to 466 // a JSAtom, or a TaggedParserAtomIndex which might reference to a non-included. 467 // 468 // The constructor for this class are using an InputScope as argument. This 469 // InputScope is made to fetch back the CompilationStencil associated with the 470 // TaggedParserAtomIndex when using a Stencil as input. 471 struct InputName { 472 using InputNameStorage = mozilla::Variant<JSAtom*, NameStencilRef>; 473 InputNameStorage variant_; 474 475 InputName(Scope*, JSAtom* ptr) : variant_(ptr) {} 476 InputName(const ScopeStencilRef& scope, TaggedParserAtomIndex index) 477 : variant_(NameStencilRef{*scope.context(), index}) {} 478 InputName(BaseScript*, JSAtom* ptr) : variant_(ptr) {} 479 InputName(const ScriptStencilRef& script, TaggedParserAtomIndex index) 480 : variant_(NameStencilRef{*script.context(), index}) {} 481 482 // Dummy for empty global. 483 InputName(const FakeStencilGlobalScope&, TaggedParserAtomIndex) 484 : variant_(static_cast<JSAtom*>(nullptr)) {} 485 486 // The InputName is either from an instantiated name, or from another 487 // CompilationStencil. This method interns the current name in the parser atom 488 // table of a CompilationState, which has a corresponding CompilationInput. 489 TaggedParserAtomIndex internInto(FrontendContext* fc, 490 ParserAtomsTable& parserAtoms, 491 CompilationAtomCache& atomCache); 492 493 // Compare an InputName, which is not yet interned, with `other` is either an 494 // interned name or a well-known or static string. 495 // 496 // The `otherCached` argument should be a reference to a JSAtom*, initialized 497 // to nullptr, which is used to cache the JSAtom representation of the `other` 498 // argument if needed. If a different `other` parameter is provided, the 499 // `otherCached` argument should be reset to nullptr. 500 bool isEqualTo(FrontendContext* fc, ParserAtomsTable& parserAtoms, 501 CompilationAtomCache& atomCache, TaggedParserAtomIndex other, 502 JSAtom** otherCached) const; 503 504 bool isNull() const { 505 return variant_.match( 506 [](JSAtom* ptr) { return !ptr; }, 507 [](const NameStencilRef& ref) { return !ref.atomIndex_; }); 508 } 509 }; 510 511 // ScopeContext holds information derived from the scope and environment chains 512 // to try to avoid the parser needing to traverse VM structures directly. 513 struct ScopeContext { 514 // Cache: Scope -> (JSAtom/TaggedParserAtomIndex -> NameLocation) 515 // 516 // This cache maps the scope to a hash table which can lookup a name of the 517 // scope to the equivalent NameLocation. 518 ScopeBindingCache* scopeCache = nullptr; 519 520 // Generation number of the `scopeCache` collected before filling the cache 521 // with enclosing scope information. 522 // 523 // The generation number, obtained from `scopeCache->getCurrentGeneration()` 524 // is incremented each time the GC invalidate the content of the cache. The 525 // `scopeCache` can only be used when the generation number collected before 526 // filling the cache is identical to the generation number seen when querying 527 // the cached content. 528 size_t scopeCacheGen = 0; 529 530 // Class field initializer info if we are nested within a class constructor. 531 // We may be an combination of arrow and eval context within the constructor. 532 mozilla::Maybe<MemberInitializers> memberInitializers = {}; 533 534 enum class EnclosingLexicalBindingKind { 535 Let, 536 Const, 537 CatchParameter, 538 Synthetic, 539 PrivateMethod, 540 }; 541 542 using EnclosingLexicalBindingCache = 543 mozilla::HashMap<TaggedParserAtomIndex, EnclosingLexicalBindingKind, 544 TaggedParserAtomIndexHasher>; 545 546 // Cache of enclosing lexical bindings. 547 // Used only for eval. 548 mozilla::Maybe<EnclosingLexicalBindingCache> enclosingLexicalBindingCache_; 549 550 // A map of private names to NameLocations used to allow evals to 551 // provide correct private name semantics (particularly around early 552 // errors and private brand lookup). 553 using EffectiveScopePrivateFieldCache = 554 mozilla::HashMap<TaggedParserAtomIndex, NameLocation, 555 TaggedParserAtomIndexHasher>; 556 557 // Cache of enclosing class's private fields. 558 // Used only for eval. 559 mozilla::Maybe<EffectiveScopePrivateFieldCache> 560 effectiveScopePrivateFieldCache_; 561 562 #ifdef DEBUG 563 bool enclosingEnvironmentIsDebugProxy_ = false; 564 #endif 565 566 // How many hops required to navigate from 'enclosingScope' to effective 567 // scope. 568 uint32_t effectiveScopeHops = 0; 569 570 uint32_t enclosingScopeEnvironmentChainLength = 0; 571 572 // Eval and arrow scripts also inherit the "this" environment -- used by 573 // `super` expressions -- from their enclosing script. We count the number of 574 // environment hops needed to get from enclosing scope to the nearest 575 // appropriate environment. This value is undefined if the script we are 576 // compiling is not an eval or arrow-function. 577 uint32_t enclosingThisEnvironmentHops = 0; 578 579 // The kind of enclosing scope. 580 ScopeKind enclosingScopeKind = ScopeKind::Global; 581 582 // The type of binding required for `this` of the top level context, as 583 // indicated by the enclosing scopes of this parse. 584 // 585 // NOTE: This is computed based on the effective scope (defined above). 586 ThisBinding thisBinding = ThisBinding::Global; 587 588 // Eval and arrow scripts inherit certain syntax allowances from their 589 // enclosing scripts. 590 bool allowNewTarget = false; 591 bool allowSuperProperty = false; 592 bool allowSuperCall = false; 593 bool allowArguments = true; 594 595 // Indicates there is a 'class' or 'with' scope on enclosing scope chain. 596 bool inClass = false; 597 bool inWith = false; 598 599 // True if the enclosing scope is for FunctionScope of arrow function. 600 bool enclosingScopeIsArrow = false; 601 602 // True if the enclosing scope has environment. 603 bool enclosingScopeHasEnvironment = false; 604 605 #ifdef DEBUG 606 // True if the enclosing scope has non-syntactic scope on chain. 607 bool hasNonSyntacticScopeOnChain = false; 608 609 // True if the enclosing scope has function scope where the function needs 610 // home object. 611 bool hasFunctionNeedsHomeObjectOnChain = false; 612 #endif 613 614 bool init(FrontendContext* fc, CompilationInput& input, 615 ParserAtomsTable& parserAtoms, ScopeBindingCache* scopeCache, 616 InheritThis inheritThis, JSObject* enclosingEnv); 617 618 mozilla::Maybe<EnclosingLexicalBindingKind> 619 lookupLexicalBindingInEnclosingScope(TaggedParserAtomIndex name); 620 621 NameLocation searchInEnclosingScope(FrontendContext* fc, 622 CompilationInput& input, 623 ParserAtomsTable& parserAtoms, 624 TaggedParserAtomIndex name); 625 626 bool effectiveScopePrivateFieldCacheHas(TaggedParserAtomIndex name); 627 mozilla::Maybe<NameLocation> getPrivateFieldLocation( 628 TaggedParserAtomIndex name); 629 630 private: 631 void computeThisBinding(const InputScope& scope); 632 void computeThisEnvironment(const InputScope& enclosingScope); 633 void computeInScope(const InputScope& enclosingScope); 634 void cacheEnclosingScope(const InputScope& enclosingScope); 635 NameLocation searchInEnclosingScopeWithCache(FrontendContext* fc, 636 CompilationInput& input, 637 ParserAtomsTable& parserAtoms, 638 TaggedParserAtomIndex name); 639 NameLocation searchInEnclosingScopeNoCache(FrontendContext* fc, 640 CompilationInput& input, 641 ParserAtomsTable& parserAtoms, 642 TaggedParserAtomIndex name); 643 644 InputScope determineEffectiveScope(InputScope& scope, JSObject* environment); 645 646 bool cachePrivateFieldsForEval(FrontendContext* fc, CompilationInput& input, 647 JSObject* enclosingEnvironment, 648 const InputScope& effectiveScope, 649 ParserAtomsTable& parserAtoms); 650 651 bool cacheEnclosingScopeBindingForEval(FrontendContext* fc, 652 CompilationInput& input, 653 ParserAtomsTable& parserAtoms); 654 655 bool addToEnclosingLexicalBindingCache(FrontendContext* fc, 656 ParserAtomsTable& parserAtoms, 657 CompilationAtomCache& atomCache, 658 InputName& name, 659 EnclosingLexicalBindingKind kind); 660 }; 661 662 struct CompilationAtomCache { 663 public: 664 using AtomCacheVector = JS::GCVector<JSString*, 0, js::SystemAllocPolicy>; 665 666 private: 667 // Atoms lowered into or converted from CompilationStencil.parserAtomData. 668 // 669 // This field is here instead of in CompilationGCOutput because atoms lowered 670 // from JSAtom is part of input (enclosing scope bindings, lazy function name, 671 // etc), and having 2 vectors in both input/output is error prone. 672 AtomCacheVector atoms_; 673 674 public: 675 JSString* getExistingStringAt(ParserAtomIndex index) const; 676 JSString* getExistingStringAt(JSContext* cx, 677 TaggedParserAtomIndex taggedIndex) const; 678 JSString* getStringAt(ParserAtomIndex index) const; 679 680 JSAtom* getExistingAtomAt(ParserAtomIndex index) const; 681 JSAtom* getExistingAtomAt(JSContext* cx, 682 TaggedParserAtomIndex taggedIndex) const; 683 JSAtom* getAtomAt(ParserAtomIndex index) const; 684 685 bool hasAtomAt(ParserAtomIndex index) const; 686 bool setAtomAt(FrontendContext* fc, ParserAtomIndex index, JSString* atom); 687 bool allocate(FrontendContext* fc, size_t length); 688 689 bool empty() const { return atoms_.empty(); } 690 size_t size() const { return atoms_.length(); } 691 692 void stealBuffer(AtomCacheVector& atoms); 693 void releaseBuffer(AtomCacheVector& atoms); 694 695 void trace(JSTracer* trc); 696 697 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 698 return atoms_.sizeOfExcludingThis(mallocSizeOf); 699 } 700 }; 701 702 // Information associated with an extra binding provided to a global script. 703 // See frontend::CompileGlobalScriptWithExtraBindings. 704 struct ExtraBindingInfo { 705 // UTF-8 encoded name of the binding. 706 UniqueChars nameChars; 707 708 TaggedParserAtomIndex nameIndex; 709 710 // If the binding conflicts with global variable or global lexical variable, 711 // the binding is shadowed. 712 bool isShadowed = false; 713 714 ExtraBindingInfo(UniqueChars&& nameChars, bool isShadowed) 715 : nameChars(std::move(nameChars)), isShadowed(isShadowed) {} 716 }; 717 718 using ExtraBindingInfoVector = 719 js::Vector<ExtraBindingInfo, 0, js::SystemAllocPolicy>; 720 721 // Input of the compilation, including source and enclosing context. 722 struct CompilationInput { 723 enum class CompilationTarget { 724 Global, 725 SelfHosting, 726 StandaloneFunction, 727 StandaloneFunctionInNonSyntacticScope, 728 Eval, 729 Module, 730 Delazification, 731 }; 732 CompilationTarget target = CompilationTarget::Global; 733 734 const JS::ReadOnlyCompileOptions& options; 735 736 CompilationAtomCache atomCache; 737 738 private: 739 InputScript lazy_ = InputScript(nullptr); 740 741 // Extra bindings for the global script. 742 ExtraBindingInfoVector* maybeExtraBindings_ = nullptr; 743 744 public: 745 RefPtr<ScriptSource> source; 746 747 // * If the target is Global, null. 748 // * If the target is SelfHosting, null. Instantiation code for self-hosting 749 // will ignore this and use the appropriate empty global scope instead. 750 // * If the target is StandaloneFunction, an empty global scope. 751 // * If the target is StandaloneFunctionInNonSyntacticScope, the non-null 752 // enclosing scope of the function 753 // * If the target is Eval, the non-null enclosing scope of the `eval`. 754 // * If the target is Module, null that means empty global scope 755 // (See EmitterScope::checkEnvironmentChainLength) 756 // * If the target is Delazification, the non-null enclosing scope of 757 // the function 758 InputScope enclosingScope = InputScope(nullptr); 759 760 explicit CompilationInput(const JS::ReadOnlyCompileOptions& options) 761 : options(options) {} 762 763 private: 764 bool initScriptSource(FrontendContext* fc); 765 766 public: 767 bool initForGlobal(FrontendContext* fc) { 768 target = CompilationTarget::Global; 769 return initScriptSource(fc); 770 } 771 772 bool initForGlobalWithExtraBindings( 773 FrontendContext* fc, ExtraBindingInfoVector* maybeExtraBindings) { 774 MOZ_ASSERT(maybeExtraBindings); 775 target = CompilationTarget::Global; 776 maybeExtraBindings_ = maybeExtraBindings; 777 return initScriptSource(fc); 778 } 779 780 bool initForSelfHostingGlobal(FrontendContext* fc) { 781 target = CompilationTarget::SelfHosting; 782 return initScriptSource(fc); 783 } 784 785 bool initForStandaloneFunction(JSContext* cx, FrontendContext* fc) { 786 target = CompilationTarget::StandaloneFunction; 787 if (!initScriptSource(fc)) { 788 return false; 789 } 790 enclosingScope = InputScope(&cx->global()->emptyGlobalScope()); 791 return true; 792 } 793 794 bool initForStandaloneFunctionInNonSyntacticScope( 795 FrontendContext* fc, JS::Handle<Scope*> functionEnclosingScope); 796 797 bool initForEval(FrontendContext* fc, JS::Handle<Scope*> evalEnclosingScope) { 798 target = CompilationTarget::Eval; 799 if (!initScriptSource(fc)) { 800 return false; 801 } 802 enclosingScope = InputScope(evalEnclosingScope); 803 return true; 804 } 805 806 bool initForModule(FrontendContext* fc) { 807 target = CompilationTarget::Module; 808 if (!initScriptSource(fc)) { 809 return false; 810 } 811 // The `enclosingScope` is the emptyGlobalScope. 812 return true; 813 } 814 815 void initFromLazy(JSContext* cx, BaseScript* lazyScript, ScriptSource* ss) { 816 MOZ_ASSERT(cx->compartment() == lazyScript->compartment()); 817 818 // We can only compile functions whose parents have previously been 819 // compiled, because compilation requires full information about the 820 // function's immediately enclosing scope. 821 MOZ_ASSERT(lazyScript->isReadyForDelazification()); 822 target = CompilationTarget::Delazification; 823 lazy_ = InputScript(lazyScript); 824 source = ss; 825 enclosingScope = lazy_.enclosingScope(); 826 } 827 828 void initFromStencil(const InitialStencilAndDelazifications& stencils, 829 ScriptIndex scriptIndex, ScriptSource* ss) { 830 target = CompilationTarget::Delazification; 831 lazy_ = InputScript(stencils, scriptIndex); 832 source = ss; 833 enclosingScope = lazy_.enclosingScope(); 834 } 835 836 // Returns true if enclosingScope field is provided to init* function, 837 // instead of setting to empty global internally. 838 bool hasNonDefaultEnclosingScope() const { 839 return target == CompilationTarget::StandaloneFunctionInNonSyntacticScope || 840 target == CompilationTarget::Eval || 841 target == CompilationTarget::Delazification; 842 } 843 844 // Returns the enclosing scope provided to init* function. 845 // nullptr otherwise. 846 InputScope maybeNonDefaultEnclosingScope() const { 847 if (hasNonDefaultEnclosingScope()) { 848 return enclosingScope; 849 } 850 return InputScope(nullptr); 851 } 852 853 // The BaseScript* is needed when instantiating a lazy function. 854 // See InstantiateTopLevel and FunctionsFromExistingLazy. 855 InputScript lazyOuterScript() { return lazy_; } 856 BaseScript* lazyOuterBaseScript() { return lazy_.raw().as<BaseScript*>(); } 857 858 // The JSFunction* is needed when instantiating a lazy function. 859 // See FunctionsFromExistingLazy. 860 JSFunction* function() const { 861 return lazy_.raw().as<BaseScript*>()->function(); 862 } 863 864 // When compiling an inner function, we want to know the unique identifier 865 // which identify a function. This is computed from the source extend. 866 SourceExtent extent() const { return lazy_.extent(); } 867 868 // See `BaseScript::immutableFlags_`. 869 ImmutableScriptFlags immutableFlags() const { return lazy_.immutableFlags(); } 870 871 RO_IMMUTABLE_SCRIPT_FLAGS(immutableFlags()) 872 873 FunctionFlags functionFlags() const { return lazy_.functionFlags(); } 874 875 // When delazifying, return the kind of function which is defined. 876 FunctionSyntaxKind functionSyntaxKind() const; 877 878 bool hasPrivateScriptData() const { 879 // This is equivalent to: ngcthings != 0 || useMemberInitializers() 880 // See BaseScript::CreateRawLazy. 881 return lazy_.hasPrivateScriptData(); 882 } 883 884 // Whether this CompilationInput is parsing the top-level of a script, or 885 // false if we are parsing an inner function. 886 bool isInitialStencil() { return lazy_.isNull(); } 887 888 // Whether this CompilationInput is parsing a specific function with already 889 // pre-parsed contextual information. 890 bool isDelazifying() { return target == CompilationTarget::Delazification; } 891 892 bool hasExtraBindings() const { return !!maybeExtraBindings_; } 893 ExtraBindingInfoVector& extraBindings() { return *maybeExtraBindings_; } 894 const ExtraBindingInfoVector& extraBindings() const { 895 return *maybeExtraBindings_; 896 } 897 bool internExtraBindings(FrontendContext* fc, ParserAtomsTable& parserAtoms); 898 899 void trace(JSTracer* trc); 900 901 // Size of dynamic data. Note that GC data is counted by GC and not here. We 902 // also ignore ScriptSource which is a shared RefPtr. 903 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 904 return atomCache.sizeOfExcludingThis(mallocSizeOf); 905 } 906 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 907 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); 908 } 909 910 #if defined(DEBUG) || defined(JS_JITSPEW) 911 void dump() const; 912 void dump(js::JSONPrinter& json) const; 913 void dumpFields(js::JSONPrinter& json) const; 914 #endif 915 }; 916 917 // When compiling a function which was previously Syntaxly Parsed, we generated 918 // some information which made it possible to skip over some parsing phases, 919 // such as computing closed over bindings as well as parsing inner functions. 920 // This class contains all information which is generated by the SyntaxParse and 921 // reused in the FullParse. 922 class CompilationSyntaxParseCache { 923 // When delazifying, we should prepare an array which contains all 924 // stencil-like gc-things such that it can be used by the parser. 925 // 926 // When compiling from a Stencil, this will alias the existing Stencil. 927 mozilla::Span<TaggedScriptThingIndex> cachedGCThings_; 928 929 // When delazifying, we should perpare an array which contains all 930 // stencil-like information about scripts, such that it can be used by the 931 // parser. 932 // 933 // When compiling from a Stencil, these will alias the existing Stencil. 934 mozilla::Span<ScriptStencil> cachedScriptData_; 935 mozilla::Span<ScriptStencilExtra> cachedScriptExtra_; 936 937 // When delazifying, we copy the atom, either from JSAtom, or from another 938 // Stencil into TaggedParserAtomIndex which are valid in this current 939 // CompilationState. 940 mozilla::Span<TaggedParserAtomIndex> closedOverBindings_; 941 942 // Atom of the function being compiled. This atom index is valid in the 943 // current CompilationState. 944 TaggedParserAtomIndex displayAtom_; 945 946 // Stencil-like data about the function which is being compiled. 947 ScriptStencilExtra funExtra_; 948 949 #ifdef DEBUG 950 // Whether any of these data should be considered or not. 951 bool isInitialized = false; 952 #endif 953 954 public: 955 // When doing a full-parse of an incomplete BaseScript*, we have to iterate 956 // over functions and closed-over bindings, to avoid costly recursive decent 957 // in inner functions. This function will clone the BaseScript* information to 958 // make it available as a stencil-like data to the full-parser. 959 mozilla::Span<TaggedParserAtomIndex> closedOverBindings() const { 960 MOZ_ASSERT(isInitialized); 961 return closedOverBindings_; 962 } 963 const ScriptStencil& scriptData(size_t functionIndex) const { 964 return cachedScriptData_[scriptIndex(functionIndex)]; 965 } 966 const ScriptStencilExtra& scriptExtra(size_t functionIndex) const { 967 return cachedScriptExtra_[scriptIndex(functionIndex)]; 968 } 969 970 // Return the name of the function being delazified, if any. 971 TaggedParserAtomIndex displayAtom() const { 972 MOZ_ASSERT(isInitialized); 973 return displayAtom_; 974 } 975 976 // Return the extra information about the function being delazified, if any. 977 const ScriptStencilExtra& funExtra() const { 978 MOZ_ASSERT(isInitialized); 979 return funExtra_; 980 } 981 982 // Initialize the SynaxParse cache given a LifoAlloc. The JSContext is only 983 // used for reporting allocation errors. 984 [[nodiscard]] bool init(FrontendContext* fc, LifoAlloc& alloc, 985 ParserAtomsTable& parseAtoms, 986 CompilationAtomCache& atomCache, 987 const InputScript& lazy); 988 989 private: 990 // Return the script index of a given inner function. 991 // 992 // WARNING: The ScriptIndex returned by this function corresponds to the index 993 // in the cachedScriptExtra_ and cachedScriptData_ spans. With the 994 // cachedGCThings_ span, these might be reference to an actual Stencil from 995 // another compilation. Thus, the ScriptIndex returned by this function should 996 // not be confused with any ScriptIndex from the CompilationState. 997 ScriptIndex scriptIndex(size_t functionIndex) const { 998 MOZ_ASSERT(isInitialized); 999 auto taggedScriptIndex = cachedGCThings_[functionIndex]; 1000 MOZ_ASSERT(taggedScriptIndex.isFunction()); 1001 return taggedScriptIndex.toFunction(); 1002 } 1003 1004 [[nodiscard]] bool copyFunctionInfo(FrontendContext* fc, 1005 ParserAtomsTable& parseAtoms, 1006 CompilationAtomCache& atomCache, 1007 const InputScript& lazy); 1008 [[nodiscard]] bool copyScriptInfo(FrontendContext* fc, LifoAlloc& alloc, 1009 ParserAtomsTable& parseAtoms, 1010 CompilationAtomCache& atomCache, 1011 BaseScript* lazy); 1012 [[nodiscard]] bool copyScriptInfo(FrontendContext* fc, LifoAlloc& alloc, 1013 ParserAtomsTable& parseAtoms, 1014 CompilationAtomCache& atomCache, 1015 const ScriptStencilRef& lazy); 1016 [[nodiscard]] bool copyClosedOverBindings(FrontendContext* fc, 1017 LifoAlloc& alloc, 1018 ParserAtomsTable& parseAtoms, 1019 CompilationAtomCache& atomCache, 1020 BaseScript* lazy); 1021 [[nodiscard]] bool copyClosedOverBindings(FrontendContext* fc, 1022 LifoAlloc& alloc, 1023 ParserAtomsTable& parseAtoms, 1024 CompilationAtomCache& atomCache, 1025 const ScriptStencilRef& lazy); 1026 }; 1027 1028 // AsmJS scripts are very rare on-average, so we use a HashMap to associate 1029 // data with a ScriptStencil. The ScriptStencil has a flag to indicate if we 1030 // need to even do this lookup. 1031 using StencilAsmJSMap = 1032 mozilla::HashMap<ScriptIndex, RefPtr<const JS::WasmModule>, 1033 mozilla::DefaultHasher<ScriptIndex>, 1034 js::SystemAllocPolicy>; 1035 1036 struct StencilAsmJSContainer 1037 : public js::AtomicRefCounted<StencilAsmJSContainer> { 1038 StencilAsmJSMap moduleMap; 1039 1040 StencilAsmJSContainer() = default; 1041 1042 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 1043 return moduleMap.shallowSizeOfExcludingThis(mallocSizeOf); 1044 } 1045 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 1046 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); 1047 } 1048 }; 1049 1050 // Store shared data for non-lazy script. 1051 struct SharedDataContainer { 1052 // NOTE: While stored, we must hold a ref-count and care must be taken when 1053 // updating or clearing the pointer. 1054 using SingleSharedDataPtr = SharedImmutableScriptData*; 1055 1056 using SharedDataVector = 1057 Vector<RefPtr<js::SharedImmutableScriptData>, 0, js::SystemAllocPolicy>; 1058 using SharedDataVectorPtr = SharedDataVector*; 1059 1060 using SharedDataMap = 1061 mozilla::HashMap<ScriptIndex, RefPtr<js::SharedImmutableScriptData>, 1062 mozilla::DefaultHasher<ScriptIndex>, 1063 js::SystemAllocPolicy>; 1064 using SharedDataMapPtr = SharedDataMap*; 1065 1066 private: 1067 enum { 1068 SingleTag = 0, 1069 VectorTag = 1, 1070 MapTag = 2, 1071 BorrowTag = 3, 1072 1073 TagMask = 3, 1074 }; 1075 1076 uintptr_t data_ = 0; 1077 1078 public: 1079 // Defaults to SingleSharedData. 1080 SharedDataContainer() = default; 1081 1082 SharedDataContainer(const SharedDataContainer&) = delete; 1083 SharedDataContainer(SharedDataContainer&& other) noexcept { 1084 std::swap(data_, other.data_); 1085 MOZ_ASSERT(other.isEmpty()); 1086 } 1087 1088 SharedDataContainer& operator=(const SharedDataContainer&) = delete; 1089 SharedDataContainer& operator=(SharedDataContainer&& other) noexcept { 1090 std::swap(data_, other.data_); 1091 MOZ_ASSERT(other.isEmpty()); 1092 return *this; 1093 } 1094 1095 ~SharedDataContainer(); 1096 1097 [[nodiscard]] bool initVector(FrontendContext* fc); 1098 [[nodiscard]] bool initMap(FrontendContext* fc); 1099 1100 private: 1101 [[nodiscard]] bool convertFromSingleToMap(FrontendContext* fc); 1102 1103 public: 1104 bool isEmpty() const { return (data_) == SingleTag; } 1105 bool isSingle() const { return (data_ & TagMask) == SingleTag; } 1106 bool isVector() const { return (data_ & TagMask) == VectorTag; } 1107 bool isMap() const { return (data_ & TagMask) == MapTag; } 1108 bool isBorrow() const { return (data_ & TagMask) == BorrowTag; } 1109 1110 void setSingle(already_AddRefed<SharedImmutableScriptData>&& data) { 1111 MOZ_ASSERT(isEmpty()); 1112 data_ = reinterpret_cast<uintptr_t>(data.take()); 1113 MOZ_ASSERT(isSingle()); 1114 MOZ_ASSERT(!isEmpty()); 1115 } 1116 1117 void setBorrow(SharedDataContainer* sharedData) { 1118 MOZ_ASSERT(isEmpty()); 1119 data_ = reinterpret_cast<uintptr_t>(sharedData) | BorrowTag; 1120 MOZ_ASSERT(isBorrow()); 1121 } 1122 1123 SingleSharedDataPtr asSingle() const { 1124 MOZ_ASSERT(isSingle()); 1125 MOZ_ASSERT(!isEmpty()); 1126 static_assert(SingleTag == 0); 1127 return reinterpret_cast<SingleSharedDataPtr>(data_); 1128 } 1129 SharedDataVectorPtr asVector() const { 1130 MOZ_ASSERT(isVector()); 1131 return reinterpret_cast<SharedDataVectorPtr>(data_ & ~TagMask); 1132 } 1133 SharedDataMapPtr asMap() const { 1134 MOZ_ASSERT(isMap()); 1135 return reinterpret_cast<SharedDataMapPtr>(data_ & ~TagMask); 1136 } 1137 SharedDataContainer* asBorrow() const { 1138 MOZ_ASSERT(isBorrow()); 1139 return reinterpret_cast<SharedDataContainer*>(data_ & ~TagMask); 1140 } 1141 1142 [[nodiscard]] bool prepareStorageFor(FrontendContext* fc, 1143 size_t nonLazyScriptCount, 1144 size_t allScriptCount); 1145 [[nodiscard]] bool cloneFrom(FrontendContext* fc, 1146 const SharedDataContainer& other); 1147 1148 // Returns index-th script's shared data, or nullptr if it doesn't have. 1149 js::SharedImmutableScriptData* get(ScriptIndex index) const; 1150 1151 // Add data for index-th script and share it with VM. 1152 [[nodiscard]] bool addAndShare(FrontendContext* fc, ScriptIndex index, 1153 js::SharedImmutableScriptData* data); 1154 1155 // Add data for index-th script without sharing it with VM. 1156 // The data should already be shared with VM. 1157 // 1158 // The data is supposed to be added from delazification. 1159 [[nodiscard]] bool addExtraWithoutShare(FrontendContext* fc, 1160 ScriptIndex index, 1161 js::SharedImmutableScriptData* data); 1162 1163 // Dynamic memory associated with this container. Does not include the 1164 // SharedImmutableScriptData since we are not the unique owner of it. 1165 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 1166 if (isVector()) { 1167 return asVector()->sizeOfIncludingThis(mallocSizeOf); 1168 } 1169 if (isMap()) { 1170 return asMap()->shallowSizeOfIncludingThis(mallocSizeOf); 1171 } 1172 MOZ_ASSERT(isSingle() || isBorrow()); 1173 return 0; 1174 } 1175 1176 #if defined(DEBUG) || defined(JS_JITSPEW) 1177 void dump() const; 1178 void dump(js::JSONPrinter& json) const; 1179 void dumpFields(js::JSONPrinter& json) const; 1180 #endif 1181 }; 1182 1183 struct ExtensibleCompilationStencil; 1184 1185 // The top level struct of stencil specialized for non-extensible case. 1186 // Used as the compilation output, and also XDR decode output. 1187 // 1188 // In XDR decode output case, the span and not-owning pointer fields point 1189 // the internal LifoAlloc and the external XDR buffer. 1190 // 1191 // In BorrowingCompilationStencil usage, span and not-owning pointer fields 1192 // point the ExtensibleCompilationStencil and its LifoAlloc. 1193 // 1194 // The dependent XDR buffer or ExtensibleCompilationStencil must be kept 1195 // alive manually. 1196 // 1197 // See SMDOC in Stencil.h for more info. 1198 struct CompilationStencil { 1199 friend struct ExtensibleCompilationStencil; 1200 1201 static constexpr ScriptIndex TopLevelIndex = ScriptIndex(0); 1202 1203 static constexpr size_t LifoAllocChunkSize = 512; 1204 1205 // The lifetime of this CompilationStencil may be managed by stack allocation, 1206 // UniquePtr<T>, or RefPtr<T>. If a RefPtr is used, this ref-count will track 1207 // the lifetime, otherwise it is ignored. 1208 // 1209 // NOTE: Internal code and public APIs use a mix of these different allocation 1210 // modes. 1211 // 1212 // See: JS::StencilAddRef/Release 1213 mutable mozilla::Atomic<uintptr_t> refCount_{0}; 1214 1215 private: 1216 // On-heap ExtensibleCompilationStencil that this CompilationStencil owns, 1217 // and this CompilationStencil borrows each data from. 1218 UniquePtr<ExtensibleCompilationStencil> ownedBorrowStencil; 1219 1220 public: 1221 enum class StorageType { 1222 // Pointers and spans point LifoAlloc or owned buffer. 1223 Owned, 1224 1225 // Pointers and spans point external data, such as XDR buffer, or not-owned 1226 // ExtensibleCompilationStencil (see BorrowingCompilationStencil). 1227 Borrowed, 1228 1229 // Pointers and spans point data owned by ownedBorrowStencil. 1230 OwnedExtensible, 1231 }; 1232 StorageType storageType = StorageType::Owned; 1233 1234 // Value of CanLazilyParse(CompilationInput) on compilation. 1235 // Used during instantiation, and also queried by 1236 // InitialStencilAndDelazifications. 1237 bool canLazilyParse = false; 1238 1239 // If this stencil is a delazification, this identifies location of the 1240 // function in the source text. 1241 using FunctionKey = SourceExtent::FunctionKey; 1242 FunctionKey functionKey = SourceExtent::NullFunctionKey; 1243 1244 // This holds allocations that do not require destructors to be run but are 1245 // live until the stencil is released. 1246 LifoAlloc alloc; 1247 1248 // The source text holder for the script. This may be an empty placeholder if 1249 // the code will fully parsed and options indicate the source will never be 1250 // needed again. 1251 RefPtr<ScriptSource> source; 1252 1253 // Stencil for all function and non-function scripts. The TopLevelIndex is 1254 // reserved for the top-level script. This top-level may or may not be a 1255 // function. 1256 mozilla::Span<ScriptStencil> scriptData; 1257 1258 // Immutable data computed during initial compilation and never updated during 1259 // delazification. 1260 mozilla::Span<ScriptStencilExtra> scriptExtra; 1261 1262 mozilla::Span<TaggedScriptThingIndex> gcThingData; 1263 1264 // scopeData and scopeNames have the same size, and i-th scopeNames contains 1265 // the names for the bindings contained in the slot defined by i-th scopeData. 1266 mozilla::Span<ScopeStencil> scopeData; 1267 mozilla::Span<BaseParserScopeData*> scopeNames; 1268 1269 // Hold onto the RegExpStencil, BigIntStencil, and ObjLiteralStencil that are 1270 // allocated during parse to ensure correct destruction. 1271 mozilla::Span<RegExpStencil> regExpData; 1272 mozilla::Span<BigIntStencil> bigIntData; 1273 mozilla::Span<ObjLiteralStencil> objLiteralData; 1274 1275 // List of parser atoms for this compilation. This may contain nullptr entries 1276 // when round-tripping with XDR if the atom was generated in original parse 1277 // but not used by stencil. 1278 ParserAtomSpan parserAtomData; 1279 1280 // Variable sized container for bytecode and other immutable data. A valid 1281 // stencil always contains at least an entry for `TopLevelIndex` script. 1282 SharedDataContainer sharedData; 1283 1284 // Module metadata if this is a module compile. 1285 RefPtr<StencilModuleMetadata> moduleMetadata; 1286 1287 // AsmJS modules generated by parsing. These scripts are never lazy and 1288 // therefore only generated during initial parse. 1289 // 1290 // This is non-null if we have seen an asm.js directive, but doesn't imply 1291 // that we successfully compiled an asm.js module. See the comment in 1292 // FunctionBox::setUseAsm for more information. 1293 RefPtr<StencilAsmJSContainer> asmJS; 1294 1295 // End of fields. 1296 1297 // Construct a CompilationStencil 1298 explicit CompilationStencil(ScriptSource* source) 1299 : alloc(LifoAllocChunkSize, js::BackgroundMallocArena), source(source) {} 1300 1301 // Take the ownership of on-heap ExtensibleCompilationStencil and 1302 // borrow from it. 1303 explicit CompilationStencil( 1304 UniquePtr<ExtensibleCompilationStencil>&& extensibleStencil); 1305 1306 void AddRef(); 1307 void Release(); 1308 1309 protected: 1310 void borrowFromExtensibleCompilationStencil( 1311 ExtensibleCompilationStencil& extensibleStencil); 1312 1313 #ifdef DEBUG 1314 void assertBorrowingFromExtensibleCompilationStencil( 1315 const ExtensibleCompilationStencil& extensibleStencil) const; 1316 #endif 1317 1318 public: 1319 bool isInitialStencil() const { 1320 return functionKey == SourceExtent::NullFunctionKey; 1321 } 1322 1323 [[nodiscard]] static bool instantiateStencilAfterPreparation( 1324 JSContext* cx, CompilationInput& input, const CompilationStencil& stencil, 1325 CompilationGCOutput& gcOutput); 1326 1327 [[nodiscard]] static bool prepareForInstantiate( 1328 FrontendContext* fc, CompilationAtomCache& atomCache, 1329 const CompilationStencil& stencil, CompilationGCOutput& gcOutput); 1330 [[nodiscard]] static bool prepareForInstantiate( 1331 FrontendContext* fc, const CompilationStencil& stencil, 1332 PreallocatedCompilationGCOutput& gcOutput); 1333 1334 [[nodiscard]] static bool instantiateStencils( 1335 JSContext* cx, CompilationInput& input, const CompilationStencil& stencil, 1336 CompilationGCOutput& gcOutput); 1337 1338 // Decode the special self-hosted stencil 1339 [[nodiscard]] bool instantiateSelfHostedAtoms( 1340 JSContext* cx, AtomSet& atomSet, CompilationAtomCache& atomCache) const; 1341 [[nodiscard]] JSScript* instantiateSelfHostedTopLevelForRealm( 1342 JSContext* cx, CompilationInput& input); 1343 [[nodiscard]] JSFunction* instantiateSelfHostedLazyFunction( 1344 JSContext* cx, CompilationAtomCache& atomCache, ScriptIndex index, 1345 JS::Handle<JSAtom*> name); 1346 [[nodiscard]] bool delazifySelfHostedFunction(JSContext* cx, 1347 CompilationAtomCache& atomCache, 1348 ScriptIndexRange range, 1349 Handle<JSAtom*> name, 1350 JS::Handle<JSFunction*> fun); 1351 1352 // To avoid any misuses, make sure this is neither copyable or assignable. 1353 CompilationStencil(const CompilationStencil&) = delete; 1354 CompilationStencil(CompilationStencil&&) = delete; 1355 CompilationStencil& operator=(const CompilationStencil&) = delete; 1356 CompilationStencil& operator=(CompilationStencil&&) = delete; 1357 #ifdef DEBUG 1358 ~CompilationStencil() { 1359 // We can mix UniquePtr<..> and RefPtr<..>. This asserts that a UniquePtr 1360 // does not delete a reference-counted stencil. 1361 MOZ_ASSERT(!refCount_); 1362 } 1363 #endif 1364 1365 static inline ScriptStencilIterable functionScriptStencils( 1366 const CompilationStencil& stencil, CompilationGCOutput& gcOutput); 1367 1368 void setFunctionKey(BaseScript* lazy) { 1369 functionKey = lazy->extent().toFunctionKey(); 1370 } 1371 1372 inline size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 1373 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 1374 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); 1375 } 1376 1377 const ParserAtomSpan& parserAtomsSpan() const { return parserAtomData; } 1378 1379 bool isModule() const; 1380 1381 // Whether this stencil has a function with an asm.js directive in it. The 1382 // function may not have been compiled with asm.js if support was disabled or 1383 // the asm.js module failed validation. 1384 bool hasAsmJS() const; 1385 1386 bool hasMultipleReference() const { return refCount_ > 1; } 1387 1388 bool hasOwnedBorrow() const { 1389 return storageType == StorageType::OwnedExtensible; 1390 } 1391 1392 ExtensibleCompilationStencil* takeOwnedBorrow() { 1393 MOZ_ASSERT(!hasMultipleReference()); 1394 MOZ_ASSERT(hasOwnedBorrow()); 1395 return ownedBorrowStencil.release(); 1396 } 1397 1398 #ifdef DEBUG 1399 void assertNoExternalDependency() const; 1400 #endif 1401 1402 #if defined(DEBUG) || defined(JS_JITSPEW) 1403 void dump() const; 1404 void dump(js::JSONPrinter& json) const; 1405 void dumpFields(js::JSONPrinter& json) const; 1406 1407 void dumpAtom(TaggedParserAtomIndex index) const; 1408 #endif 1409 }; 1410 1411 // A Map from a function key to the ScriptIndex in the initial stencil. 1412 class FunctionKeyToScriptIndexMap { 1413 using FunctionKey = SourceExtent::FunctionKey; 1414 mozilla::HashMap<FunctionKey, ScriptIndex, 1415 mozilla::DefaultHasher<FunctionKey>, js::SystemAllocPolicy> 1416 map_; 1417 1418 template <typename T> 1419 [[nodiscard]] bool init(FrontendContext* fc, const T& scriptExtra, 1420 size_t scriptExtraSize); 1421 1422 public: 1423 FunctionKeyToScriptIndexMap() = default; 1424 1425 [[nodiscard]] bool init(FrontendContext* fc, 1426 const CompilationStencil* initial); 1427 [[nodiscard]] bool init(FrontendContext* fc, 1428 const ExtensibleCompilationStencil* initial); 1429 1430 mozilla::Maybe<ScriptIndex> get(FunctionKey key) const; 1431 1432 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 1433 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 1434 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); 1435 } 1436 }; 1437 1438 // This structure represents a function script inside a function, using a pair 1439 // of indices which points to the enclosing script and the possibly-local index 1440 // inside the enclosing compilation stencil. 1441 // 1442 // The `enclosingIndexInInitial` represents both the function script index in 1443 // `InitialStencilAndDelazifications::initial_` and the stencil index within 1444 // `InitialStencilAndDelazifications::delazifications_`, unless it is 0, in 1445 // which case it corresponds to the initial stencil. 1446 // 1447 // The `indexInEnclosing` represents the script index in the compilation stencil 1448 // in which the enclosing script would be fully parsed, indexed by 1449 // `enclosingIndexInInitial`, which can either be the initial stencil or a 1450 // delazification stencil. 1451 // 1452 // This structure absorbs the `InitialStencilAndDelazifications` splitted 1453 // representation of having multiple `CompilationStencil`. Among the differences 1454 // we can note the followings special case: 1455 // 1456 // (a) When `enclosingIndexInInitial` points a function which is eagerly 1457 // compiled in the initial compilation, then the `indexInEnclosing` is also an 1458 // index in the initial compilation, which points an inner function inside the 1459 // enclosing function. Thus, `indexInEnclosing > enclosingIndexInInitial`. 1460 // 1461 // (b) When `enclosingIndexInInitial` points a function which is syntax-parsed 1462 // in the initial compilation, then the `indexInEnclosing` is an index in a 1463 // delazification compilation. Given that the index 0-th of a delazification 1464 // compilation is the delazified script. Thus, `indexInEnclosing > 0`, which 1465 // points an inner function inside the enclosing function. 1466 // 1467 // At the intersection of both cases, the followings should be held: 1468 // 1469 // - `indexInEnclosing != 0`: it cannot point neither of the top-level script, 1470 // or 1471 // the enclosing function script itself. 1472 struct ScriptIndexes { 1473 // Index of an enclosing function script within the initial 1474 // CompilationStencil. This is used also to retrieve the initial stencil or 1475 // delazification stencil from `InitialStencilAndDelazifications`. 1476 ScriptIndex enclosingIndexInInitial; 1477 1478 // Index of a function script within the enclosing `CompilationStencil`. The 1479 // enclosing `CompilationStencil` contains the fully-parsed content for the 1480 // `enclosingIndexInInitial` function script. It can either be the initial 1481 // compilation stencil or the delazitication. 1482 ScriptIndex indexInEnclosing; 1483 }; 1484 1485 struct RelativeIndexes { 1486 // Number of tasks which are making use of the indexes_ vector. 1487 ExclusiveData<size_t> consumers_; 1488 1489 // The consumers_ lock must be held and should have at most a single consumer 1490 // to write to this value. In the spirit, this should be an RWExclusiveData 1491 // using the same lock as the consumers_, except that we do not want to hold 1492 // the lock when we are in a section where the consumer value is positive. 1493 Vector<ScriptIndexes, 0, js::SystemAllocPolicy> indexes_; 1494 1495 RelativeIndexes() : consumers_(mutexid::StencilCache), indexes_() {} 1496 1497 ScriptIndexes& operator[](size_t i) { return indexes_[i]; } 1498 const ScriptIndexes& operator[](size_t i) const { return indexes_[i]; } 1499 }; 1500 1501 // A class to Associate the initial stencil and the delazifications. 1502 // 1503 // This struct is initialized with the initial stencil, with an empty set of 1504 // delazifications. 1505 // The delazifications_ vector is fixed-size, pre-allocated for each script 1506 // stencil, excluding the top-level script. 1507 // 1508 // The delazifications_ vector elements are initialized with nullptr, and 1509 // monotonically populated with each delazification result. 1510 // Only the first delazification for the given function is used. 1511 // 1512 // This struct is supposed to be read/write from multiple threads, and all 1513 // operations, except init, are thread-safe. 1514 struct InitialStencilAndDelazifications { 1515 private: 1516 using FunctionKey = SourceExtent::FunctionKey; 1517 1518 // Shared reference to the initial stencil. 1519 RefPtr<const CompilationStencil> initial_; 1520 1521 // Exclusively owning pointers for delazifications. 1522 // 1523 // The i-th element is for ScriptIndex(i-1). 1524 // 1525 // If the initial stencil is known to be fully-parsed, this vector is 1526 // 0-sized and unused. 1527 Vector<mozilla::Atomic<CompilationStencil*>, 0, js::SystemAllocPolicy> 1528 delazifications_; 1529 1530 // A Map from a function key to the ScriptIndex in the initial stencil. 1531 // 1532 // If the initial stencil is known to be fully-parsed, this map is 1533 // uninitialized and unused 1534 FunctionKeyToScriptIndexMap functionKeyToInitialScriptIndex_; 1535 1536 // Map an initial scriptIndex to its enclosing initial scriptIndex as well as 1537 // its scriptIndex within the enclosing script stencil. 1538 // 1539 // This structure caches information which is only available after some 1540 // computation in a stencil. 1541 // 1542 // For example, the following script: 1543 // ```js 1544 // function f1() { 1545 // function f2() { 1546 // function f3() {} 1547 // function f4() {} 1548 // } 1549 // function f5() {} 1550 // } 1551 // ``` 1552 // 1553 // Would yield a vector such as: 1554 // 1555 // relativeIndexes_ = { 1556 // /* f1 */ { enclosingIndexInInitial: 0, indexInEnclosing: 1 }, 1557 // /* f2 */ { enclosingIndexInInitial: 1, indexInEnclosing: 1 }, 1558 // /* f3 */ { enclosingIndexInInitial: 2, indexInEnclosing: 1 }, 1559 // /* f4 */ { enclosingIndexInInitial: 2, indexInEnclosing: 2 }, 1560 // /* f5 */ { enclosingIndexInInitial: 1, indexInEnclosing: 2 }, 1561 // } 1562 // 1563 RelativeIndexes relativeIndexes_; 1564 1565 mutable mozilla::Atomic<uintptr_t> refCount_{0}; 1566 1567 public: 1568 class RelativeIndexesGuard { 1569 friend struct InitialStencilAndDelazifications; 1570 RefPtr<InitialStencilAndDelazifications> stencils_; 1571 1572 explicit RelativeIndexesGuard(InitialStencilAndDelazifications* stencils) 1573 : stencils_(stencils) {} 1574 1575 public: 1576 RelativeIndexesGuard() : stencils_(nullptr) {} 1577 1578 RelativeIndexesGuard(RelativeIndexesGuard&& src) 1579 : stencils_(std::move(src.stencils_)) {} 1580 1581 ~RelativeIndexesGuard() { 1582 if (stencils_) { 1583 stencils_->decrementRelativeIndexesConsumer(); 1584 stencils_ = nullptr; 1585 } 1586 }; 1587 explicit operator bool() { return bool(stencils_); } 1588 }; 1589 1590 private: 1591 // Initialize relative indexes based on the initial's gcThings. 1592 void decrementRelativeIndexesConsumer(); 1593 friend class RelativeIndexesGuard; 1594 1595 public: 1596 InitialStencilAndDelazifications() = default; 1597 ~InitialStencilAndDelazifications(); 1598 1599 void AddRef(); 1600 void Release(); 1601 1602 [[nodiscard]] bool init(FrontendContext* fc, 1603 const CompilationStencil* initial); 1604 1605 [[nodiscard]] RelativeIndexesGuard ensureRelativeIndexes(FrontendContext* fc); 1606 1607 // Get the initial stencil. 1608 // As long as this instance is initialized, this returns non-null pointer. 1609 const CompilationStencil* getInitial() const; 1610 1611 // Returns true if the initial stencil is compiled with 1612 // CanLazilyParse(CompilationInput). 1613 // 1614 // If this returns false: 1615 // * the delazifications_ vector is not allocated 1616 // * the functionKeyToInitialScriptIndex_ is not initialized 1617 // * getDelazificationAt and storeDelazification shouldn't be called 1618 // * getMerged shouldn't be called, and getInitial should be used instead 1619 bool canLazilyParse() const { return initial_->canLazilyParse; } 1620 1621 // Return the delazification stencil if it's already populated. 1622 // Returns nullptr otherwise. 1623 // 1624 // The functionIndex parameter is the index of the corresponding script 1625 // stencil (0-indexed, with the index 0 being the top-level script). 1626 // 1627 // if the extent is used instead, it calculates functionIndex and returns 1628 // the delazification stencil if the functionIndex is found and it's already 1629 // populated. 1630 // Returns nullptr otherwise. 1631 const CompilationStencil* getDelazificationAt(size_t functionIndex) const; 1632 const CompilationStencil* getDelazificationFor( 1633 const SourceExtent& extent) const; 1634 1635 // Return the ScriptIndex of a delazification stencil in the initial stencil. 1636 // This index is retrieved using the SourceExtent. 1637 ScriptIndex getScriptIndexFor(const CompilationStencil* delazification) const; 1638 1639 // Return the script indexes to find the enclosing function script's index in 1640 // the initial stencil as well as the ScriptIndex within the enclosing 1641 // stencil. 1642 const ScriptIndexes& getRelativeIndexesAt(ScriptIndex initialIndex) const; 1643 1644 // Return the initial scriptIndex corresponding to the `enclosedInEnclosing` 1645 // scriptIndex in the `CompilationStencil` indexed by `enclosingInInitial` in 1646 // the `InitialStencilAndDelazifications`. 1647 // 1648 // Special cases: 1649 // 1650 // - For zero-values of `enclosedInEnclosing`, then the `enclosedInEnclosing` 1651 // index points at the first script of the `CompilationStencil` indexed by 1652 // `enclosingInInitial`. The 0-th script of any `CompilationStencil` is the 1653 // top-level or the function it-self. Thus `enclosingInInitial` is returned. 1654 // 1655 // - For cases where the script indexed by `enclosingInInitial` is compiled in 1656 // the initial stencil, then the index `enclosedInEnclosing` is already a 1657 // ScriptIndex in the initial CompilationStencil. Thus this function simply 1658 // return `enclosedInEnclosing`. 1659 // 1660 // Note: 1661 // 1662 // For non-zero values of `enclosedInEnclosing`, this function behaves as the 1663 // reverse mapping of `getRelativeIndexesAt`. 1664 ScriptIndex getInitialIndexFor(ScriptIndex enclosingInInitial, 1665 ScriptIndex enclosedInEnclosing) const; 1666 1667 // Try storing the delazification stencil. 1668 // 1669 // The `delazification` stencil should have only one ref count. 1670 // 1671 // If the function was not yet delazified and populated, the `delazification` 1672 // is stored into the vector and the ownership is transferred to the vector, 1673 // and the same `delazification`'s pointer is returned. 1674 // 1675 // If the function was already delazified and stored, the passed 1676 // `delazification` is discared, and the already delazified stencil's pointer 1677 // is returned. 1678 // 1679 // This function is infallible and never returns nullptr. 1680 const CompilationStencil* storeDelazification( 1681 RefPtr<CompilationStencil>&& delazification); 1682 1683 // Create single CompilationStencil that reflects the initial stencil 1684 // and the all delazifications. 1685 // 1686 // Returns nullptr if any error happens, and sets exception on the 1687 // FrontendContext. 1688 CompilationStencil* getMerged(FrontendContext* fc) const; 1689 1690 // Whether this stencil has a function with an asm.js directive in it. The 1691 // function may not have been compiled with asm.js if support was disabled or 1692 // the asm.js module failed validation. 1693 bool hasAsmJS() const; 1694 1695 // Instantiate the initial stencil and all delazifications populated so far. 1696 [[nodiscard]] static bool instantiateStencils( 1697 JSContext* cx, CompilationInput& input, 1698 InitialStencilAndDelazifications& stencils, 1699 CompilationGCOutput& gcOutput); 1700 1701 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 1702 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 1703 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); 1704 } 1705 1706 #if defined(DEBUG) || defined(JS_JITSPEW) 1707 void dump() const; 1708 void dump(js::JSONPrinter& json) const; 1709 void dumpFields(js::JSONPrinter& json) const; 1710 #endif 1711 }; 1712 1713 // The top level struct of stencil specialized for extensible case. 1714 // Used as the temporary storage during compilation, an the compilation output. 1715 // 1716 // All not-owning pointer fields point the internal LifoAlloc. 1717 // 1718 // See CompilationStencil for each field's description. 1719 // 1720 // Also see SMDOC in Stencil.h for more info. 1721 struct ExtensibleCompilationStencil { 1722 bool canLazilyParse = false; 1723 1724 using FunctionKey = SourceExtent::FunctionKey; 1725 1726 FunctionKey functionKey = SourceExtent::NullFunctionKey; 1727 1728 // Data pointed by other fields are allocated in this LifoAlloc, 1729 // and moved to `CompilationStencil.alloc`. 1730 LifoAlloc alloc; 1731 1732 RefPtr<ScriptSource> source; 1733 1734 // NOTE: We reserve a modest amount of inline storage in order to reduce 1735 // allocations in the most common delazification cases. These common 1736 // cases have one script and scope, as well as a handful of gcthings. 1737 // For complex pages this covers about 75% of delazifications. 1738 1739 Vector<ScriptStencil, 1, js::SystemAllocPolicy> scriptData; 1740 Vector<ScriptStencilExtra, 0, js::SystemAllocPolicy> scriptExtra; 1741 1742 Vector<TaggedScriptThingIndex, 8, js::SystemAllocPolicy> gcThingData; 1743 1744 Vector<ScopeStencil, 1, js::SystemAllocPolicy> scopeData; 1745 Vector<BaseParserScopeData*, 1, js::SystemAllocPolicy> scopeNames; 1746 1747 Vector<RegExpStencil, 0, js::SystemAllocPolicy> regExpData; 1748 BigIntStencilVector bigIntData; 1749 Vector<ObjLiteralStencil, 0, js::SystemAllocPolicy> objLiteralData; 1750 1751 // Table of parser atoms for this compilation. 1752 ParserAtomsTable parserAtoms; 1753 1754 SharedDataContainer sharedData; 1755 1756 RefPtr<StencilModuleMetadata> moduleMetadata; 1757 1758 // AsmJS modules generated by parsing. These scripts are never lazy and 1759 // therefore only generated during initial parse. 1760 // 1761 // This is non-null if we have seen an asm.js directive, but doesn't imply 1762 // that we successfully compiled an asm.js module. See the comment in 1763 // FunctionBox::setUseAsm for more information. 1764 RefPtr<StencilAsmJSContainer> asmJS; 1765 1766 explicit ExtensibleCompilationStencil(ScriptSource* source); 1767 1768 explicit ExtensibleCompilationStencil(CompilationInput& input); 1769 ExtensibleCompilationStencil(const JS::ReadOnlyCompileOptions& options, 1770 RefPtr<ScriptSource> source); 1771 1772 ExtensibleCompilationStencil(ExtensibleCompilationStencil&& other) noexcept 1773 : canLazilyParse(other.canLazilyParse), 1774 functionKey(other.functionKey), 1775 alloc(CompilationStencil::LifoAllocChunkSize, 1776 js::BackgroundMallocArena), 1777 source(std::move(other.source)), 1778 scriptData(std::move(other.scriptData)), 1779 scriptExtra(std::move(other.scriptExtra)), 1780 gcThingData(std::move(other.gcThingData)), 1781 scopeData(std::move(other.scopeData)), 1782 scopeNames(std::move(other.scopeNames)), 1783 regExpData(std::move(other.regExpData)), 1784 bigIntData(std::move(other.bigIntData)), 1785 objLiteralData(std::move(other.objLiteralData)), 1786 parserAtoms(std::move(other.parserAtoms)), 1787 sharedData(std::move(other.sharedData)), 1788 moduleMetadata(std::move(other.moduleMetadata)), 1789 asmJS(std::move(other.asmJS)) { 1790 alloc.steal(&other.alloc); 1791 parserAtoms.fixupAlloc(alloc); 1792 } 1793 1794 ExtensibleCompilationStencil& operator=( 1795 ExtensibleCompilationStencil&& other) noexcept { 1796 MOZ_ASSERT(alloc.isEmpty()); 1797 1798 canLazilyParse = other.canLazilyParse; 1799 functionKey = other.functionKey; 1800 source = std::move(other.source); 1801 scriptData = std::move(other.scriptData); 1802 scriptExtra = std::move(other.scriptExtra); 1803 gcThingData = std::move(other.gcThingData); 1804 scopeData = std::move(other.scopeData); 1805 scopeNames = std::move(other.scopeNames); 1806 regExpData = std::move(other.regExpData); 1807 bigIntData = std::move(other.bigIntData); 1808 objLiteralData = std::move(other.objLiteralData); 1809 parserAtoms = std::move(other.parserAtoms); 1810 sharedData = std::move(other.sharedData); 1811 moduleMetadata = std::move(other.moduleMetadata); 1812 asmJS = std::move(other.asmJS); 1813 1814 alloc.steal(&other.alloc); 1815 parserAtoms.fixupAlloc(alloc); 1816 1817 return *this; 1818 } 1819 1820 void setFunctionKey(const SourceExtent& extent) { 1821 functionKey = extent.toFunctionKey(); 1822 } 1823 1824 bool isInitialStencil() const { 1825 return functionKey == SourceExtent::NullFunctionKey; 1826 } 1827 1828 // Steal CompilationStencil content. 1829 [[nodiscard]] bool steal(FrontendContext* fc, 1830 RefPtr<CompilationStencil>&& other); 1831 1832 // Clone ExtensibleCompilationStencil content. 1833 [[nodiscard]] bool cloneFrom(FrontendContext* fc, 1834 const CompilationStencil& other); 1835 [[nodiscard]] bool cloneFrom(FrontendContext* fc, 1836 const ExtensibleCompilationStencil& other); 1837 1838 private: 1839 template <typename Stencil> 1840 [[nodiscard]] bool cloneFromImpl(FrontendContext* fc, const Stencil& other); 1841 1842 public: 1843 const ParserAtomVector& parserAtomsSpan() const { 1844 return parserAtoms.entries(); 1845 } 1846 1847 bool isModule() const; 1848 1849 // Whether this stencil has a function with an asm.js directive in it. The 1850 // function may not have been compiled with asm.js if support was disabled or 1851 // the asm.js module failed validation. 1852 bool hasAsmJS() const; 1853 1854 inline size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 1855 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 1856 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); 1857 } 1858 1859 #ifdef DEBUG 1860 void assertNoExternalDependency() const; 1861 #endif 1862 1863 #if defined(DEBUG) || defined(JS_JITSPEW) 1864 void dump(); 1865 void dump(js::JSONPrinter& json); 1866 void dumpFields(js::JSONPrinter& json); 1867 1868 void dumpAtom(TaggedParserAtomIndex index); 1869 #endif 1870 }; 1871 1872 // The internal state of the compilation. 1873 struct MOZ_RAII CompilationState : public ExtensibleCompilationStencil { 1874 Directives directives; 1875 1876 ScopeContext scopeContext; 1877 1878 UsedNameTracker usedNames; 1879 1880 // LifoAlloc scope used by Parser for allocating AST etc. 1881 // 1882 // NOTE: This is not used for ExtensibleCompilationStencil.alloc. 1883 LifoAllocScope& parserAllocScope; 1884 1885 CompilationInput& input; 1886 CompilationSyntaxParseCache previousParseCache; 1887 1888 // The number of functions that *will* have bytecode. 1889 // This doesn't count top-level non-function script. 1890 // 1891 // This should be counted while parsing, and should be passed to 1892 // SharedDataContainer.prepareStorageFor *before* start emitting bytecode. 1893 size_t nonLazyFunctionCount = 0; 1894 1895 // End of fields. 1896 1897 CompilationState(FrontendContext* fc, LifoAllocScope& parserAllocScope, 1898 CompilationInput& input); 1899 1900 bool init(FrontendContext* fc, ScopeBindingCache* scopeCache, 1901 InheritThis inheritThis = InheritThis::No, 1902 JSObject* enclosingEnv = nullptr) { 1903 if (!scopeContext.init(fc, input, parserAtoms, scopeCache, inheritThis, 1904 enclosingEnv)) { 1905 return false; 1906 } 1907 1908 // gcThings is later used by the full parser initialization. 1909 if (input.isDelazifying()) { 1910 InputScript lazy = input.lazyOuterScript(); 1911 auto& atomCache = input.atomCache; 1912 if (!previousParseCache.init(fc, alloc, parserAtoms, atomCache, lazy)) { 1913 return false; 1914 } 1915 } 1916 1917 return true; 1918 } 1919 1920 // Track the state of key allocations and roll them back as parts of parsing 1921 // get retried. This ensures iteration during stencil instantiation does not 1922 // encounter discarded frontend state. 1923 struct CompilationStatePosition { 1924 // Temporarily share this token struct with CompilationState. 1925 size_t scriptDataLength = 0; 1926 1927 size_t asmJSCount = 0; 1928 }; 1929 1930 bool prepareSharedDataStorage(FrontendContext* fc); 1931 1932 CompilationStatePosition getPosition(); 1933 void rewind(const CompilationStatePosition& pos); 1934 1935 // When parsing arrow function, parameter is parsed twice, and if there are 1936 // functions inside parameter expression, stencils will be created for them. 1937 // 1938 // Those functions exist only for lazy parsing. 1939 // Mark them "ghost", so that they don't affect other parts. 1940 // 1941 // See GHOST_FUNCTION in FunctionFlags.h for more details. 1942 void markGhost(const CompilationStatePosition& pos); 1943 1944 // Allocate space for `length` gcthings, and return the address of the 1945 // first element to `cursor` to initialize on the caller. 1946 bool allocateGCThingsUninitialized(FrontendContext* fc, 1947 ScriptIndex scriptIndex, size_t length, 1948 TaggedScriptThingIndex** cursor); 1949 1950 bool appendScriptStencilAndData(FrontendContext* fc); 1951 1952 bool appendGCThings(FrontendContext* fc, ScriptIndex scriptIndex, 1953 mozilla::Span<const TaggedScriptThingIndex> things); 1954 }; 1955 1956 // A temporary CompilationStencil instance that borrows 1957 // ExtensibleCompilationStencil data. 1958 // Ensure that this instance does not outlive the ExtensibleCompilationStencil. 1959 class MOZ_STACK_CLASS BorrowingCompilationStencil : public CompilationStencil { 1960 public: 1961 explicit BorrowingCompilationStencil( 1962 ExtensibleCompilationStencil& extensibleStencil); 1963 }; 1964 1965 // Size of dynamic data. Ignores Spans (unless their contents are in the 1966 // LifoAlloc) and RefPtrs since we are not the unique owner. 1967 inline size_t CompilationStencil::sizeOfExcludingThis( 1968 mozilla::MallocSizeOf mallocSizeOf) const { 1969 if (ownedBorrowStencil) { 1970 return ownedBorrowStencil->sizeOfIncludingThis(mallocSizeOf); 1971 } 1972 1973 size_t moduleMetadataSize = 1974 moduleMetadata ? moduleMetadata->sizeOfIncludingThis(mallocSizeOf) : 0; 1975 size_t asmJSSize = asmJS ? asmJS->sizeOfIncludingThis(mallocSizeOf) : 0; 1976 1977 return alloc.sizeOfExcludingThis(mallocSizeOf) + 1978 sharedData.sizeOfExcludingThis(mallocSizeOf) + moduleMetadataSize + 1979 asmJSSize; 1980 } 1981 1982 inline size_t ExtensibleCompilationStencil::sizeOfExcludingThis( 1983 mozilla::MallocSizeOf mallocSizeOf) const { 1984 size_t moduleMetadataSize = 1985 moduleMetadata ? moduleMetadata->sizeOfIncludingThis(mallocSizeOf) : 0; 1986 size_t asmJSSize = asmJS ? asmJS->sizeOfIncludingThis(mallocSizeOf) : 0; 1987 1988 return alloc.sizeOfExcludingThis(mallocSizeOf) + 1989 scriptData.sizeOfExcludingThis(mallocSizeOf) + 1990 scriptExtra.sizeOfExcludingThis(mallocSizeOf) + 1991 gcThingData.sizeOfExcludingThis(mallocSizeOf) + 1992 scopeData.sizeOfExcludingThis(mallocSizeOf) + 1993 scopeNames.sizeOfExcludingThis(mallocSizeOf) + 1994 regExpData.sizeOfExcludingThis(mallocSizeOf) + 1995 bigIntData.sizeOfExcludingThis(mallocSizeOf) + 1996 objLiteralData.sizeOfExcludingThis(mallocSizeOf) + 1997 parserAtoms.sizeOfExcludingThis(mallocSizeOf) + 1998 sharedData.sizeOfExcludingThis(mallocSizeOf) + moduleMetadataSize + 1999 asmJSSize; 2000 } 2001 2002 // A PreAllocateableGCArray is an array of GC thing pointers. 2003 // 2004 // The array's internal buffer can be allocated ahead of time, possibly off 2005 // main thread. 2006 template <typename T> 2007 struct PreAllocateableGCArray { 2008 private: 2009 size_t length_ = 0; 2010 2011 // Inline element for the case when length_ == 1. 2012 T inlineElem_; 2013 2014 // Heap-allocated elements for the case when length_ > 1; 2015 T* elems_ = nullptr; 2016 2017 public: 2018 struct Preallocated { 2019 private: 2020 size_t length_ = 0; 2021 uintptr_t* elems_ = nullptr; 2022 2023 friend struct PreAllocateableGCArray<T>; 2024 2025 public: 2026 Preallocated() = default; 2027 ~Preallocated(); 2028 2029 bool empty() const { return length_ == 0; } 2030 2031 size_t length() const { return length_; } 2032 2033 private: 2034 bool isInline() const { return length_ == 1; } 2035 2036 public: 2037 bool allocate(size_t length); 2038 2039 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 2040 return sizeof(uintptr_t) * length_; 2041 } 2042 }; 2043 2044 PreAllocateableGCArray() { 2045 static_assert(std::is_pointer_v<T>, 2046 "PreAllocateableGCArray element must be a pointer"); 2047 } 2048 ~PreAllocateableGCArray(); 2049 2050 bool empty() const { return length_ == 0; } 2051 2052 size_t length() const { return length_; } 2053 2054 private: 2055 bool isInline() const { return length_ == 1; } 2056 2057 public: 2058 bool allocate(size_t length); 2059 bool allocateWith(T init, size_t length); 2060 2061 // Steal pre-allocated buffer. 2062 void steal(Preallocated&& buffer); 2063 2064 T& operator[](size_t index) { 2065 MOZ_ASSERT(index < length_); 2066 2067 if (isInline()) { 2068 return inlineElem_; 2069 } 2070 2071 return elems_[index]; 2072 } 2073 const T& operator[](size_t index) const { 2074 MOZ_ASSERT(index < length_); 2075 2076 if (isInline()) { 2077 return inlineElem_; 2078 } 2079 2080 return elems_[index]; 2081 } 2082 2083 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 2084 if (!elems_) { 2085 return 0; 2086 } 2087 2088 return sizeof(T) * length_; 2089 } 2090 2091 void trace(JSTracer* trc); 2092 }; 2093 2094 struct CompilationGCOutput; 2095 2096 // Pre-allocated storage for CompilationGCOutput. 2097 struct PreallocatedCompilationGCOutput { 2098 private: 2099 PreAllocateableGCArray<JSFunction*>::Preallocated functions; 2100 PreAllocateableGCArray<js::Scope*>::Preallocated scopes; 2101 2102 friend struct CompilationGCOutput; 2103 2104 public: 2105 PreallocatedCompilationGCOutput() = default; 2106 2107 [[nodiscard]] bool allocate(FrontendContext* fc, size_t scriptDataLength, 2108 size_t scopeDataLength) { 2109 if (!functions.allocate(scriptDataLength)) { 2110 ReportOutOfMemory(fc); 2111 return false; 2112 } 2113 if (!scopes.allocate(scopeDataLength)) { 2114 ReportOutOfMemory(fc); 2115 return false; 2116 } 2117 return true; 2118 } 2119 2120 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 2121 return functions.sizeOfExcludingThis(mallocSizeOf) + 2122 scopes.sizeOfExcludingThis(mallocSizeOf); 2123 } 2124 }; 2125 2126 // The output of GC allocation from stencil. 2127 struct CompilationGCOutput { 2128 // The resulting outermost script for the compilation powered 2129 // by this CompilationStencil. 2130 JSScript* script = nullptr; 2131 2132 // The resulting module object if there is one. 2133 ModuleObject* module = nullptr; 2134 2135 // An array to handle tracing of JSFunction* and Atoms within. 2136 // 2137 // If the top level script isn't a function, the item at TopLevelIndex is 2138 // nullptr. 2139 PreAllocateableGCArray<JSFunction*> functions; 2140 2141 // References to scopes are controlled via AbstractScopePtr, which holds onto 2142 // an index (and CompilationStencil reference). 2143 PreAllocateableGCArray<js::Scope*> scopes; 2144 2145 // The result ScriptSourceObject. This is unused in delazifying parses. 2146 ScriptSourceObject* sourceObject = nullptr; 2147 2148 private: 2149 // If we are only instantiating part of a stencil, we can reduce allocations 2150 // by setting a base index and allocating only the array elements we need. 2151 // This applies to both the `functions` and `scopes` arrays. These fields are 2152 // initialized by `ensureAllocatedWithBaseIndex` which also allocates the 2153 // array appropriately. 2154 // 2155 // Note: These are only used for self-hosted delazification currently. 2156 ScriptIndex functionsBaseIndex{}; 2157 ScopeIndex scopesBaseIndex{}; 2158 2159 // End of fields. 2160 2161 public: 2162 CompilationGCOutput() = default; 2163 2164 // Helper to access the `functions` array. The NoBaseIndex version is used if 2165 // the caller never uses a base index. 2166 JSFunction*& getFunction(ScriptIndex index) { 2167 return functions[index - functionsBaseIndex]; 2168 } 2169 JSFunction*& getFunctionNoBaseIndex(ScriptIndex index) { 2170 MOZ_ASSERT(!functionsBaseIndex); 2171 return functions[index]; 2172 } 2173 2174 // Helper accessors for the `scopes` array. 2175 js::Scope*& getScope(ScopeIndex index) { 2176 return scopes[index - scopesBaseIndex]; 2177 } 2178 js::Scope*& getScopeNoBaseIndex(ScopeIndex index) { 2179 MOZ_ASSERT(!scopesBaseIndex); 2180 return scopes[index]; 2181 } 2182 js::Scope* getScopeNoBaseIndex(ScopeIndex index) const { 2183 MOZ_ASSERT(!scopesBaseIndex); 2184 return scopes[index]; 2185 } 2186 2187 // Allocate output arrays. 2188 [[nodiscard]] bool ensureAllocated(FrontendContext* fc, 2189 size_t scriptDataLength, 2190 size_t scopeDataLength) { 2191 if (functions.empty()) { 2192 if (!functions.allocate(scriptDataLength)) { 2193 ReportOutOfMemory(fc); 2194 return false; 2195 } 2196 } 2197 if (scopes.empty()) { 2198 if (!scopes.allocate(scopeDataLength)) { 2199 ReportOutOfMemory(fc); 2200 return false; 2201 } 2202 } 2203 return true; 2204 } 2205 2206 // Steal output arrays' buffer. 2207 void steal(PreallocatedCompilationGCOutput&& pre) { 2208 functions.steal(std::move(pre.functions)); 2209 scopes.steal(std::move(pre.scopes)); 2210 } 2211 2212 // A variant of `ensureAllocated` that sets a base index for the function and 2213 // scope arrays. This is used when instantiating only a subset of the stencil. 2214 // Currently this only applies to self-hosted delazification. The ranges 2215 // include the start index and exclude the limit index. 2216 [[nodiscard]] bool ensureAllocatedWithBaseIndex(FrontendContext* fc, 2217 ScriptIndex scriptStart, 2218 ScriptIndex scriptLimit, 2219 ScopeIndex scopeStart, 2220 ScopeIndex scopeLimit) { 2221 this->functionsBaseIndex = scriptStart; 2222 this->scopesBaseIndex = scopeStart; 2223 2224 return ensureAllocated(fc, scriptLimit - scriptStart, 2225 scopeLimit - scopeStart); 2226 } 2227 2228 // Size of dynamic data. Note that GC data is counted by GC and not here. 2229 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 2230 return functions.sizeOfExcludingThis(mallocSizeOf) + 2231 scopes.sizeOfExcludingThis(mallocSizeOf); 2232 } 2233 2234 void trace(JSTracer* trc); 2235 }; 2236 2237 // Iterator over functions that make up a CompilationStencil. This abstracts 2238 // over the parallel arrays in stencil and gc-output that use the same index 2239 // system. 2240 class ScriptStencilIterable { 2241 public: 2242 class ScriptAndFunction { 2243 public: 2244 const ScriptStencil& script; 2245 const ScriptStencilExtra* scriptExtra; 2246 JSFunction* function; 2247 ScriptIndex index; 2248 2249 ScriptAndFunction() = delete; 2250 ScriptAndFunction(const ScriptStencil& script, 2251 const ScriptStencilExtra* scriptExtra, 2252 JSFunction* function, ScriptIndex index) 2253 : script(script), 2254 scriptExtra(scriptExtra), 2255 function(function), 2256 index(index) {} 2257 }; 2258 2259 class Iterator { 2260 size_t index_ = 0; 2261 const CompilationStencil& stencil_; 2262 CompilationGCOutput& gcOutput_; 2263 2264 Iterator(const CompilationStencil& stencil, CompilationGCOutput& gcOutput, 2265 size_t index) 2266 : index_(index), stencil_(stencil), gcOutput_(gcOutput) { 2267 MOZ_ASSERT(index == stencil.scriptData.size()); 2268 } 2269 2270 public: 2271 explicit Iterator(const CompilationStencil& stencil, 2272 CompilationGCOutput& gcOutput) 2273 : stencil_(stencil), gcOutput_(gcOutput) { 2274 skipTopLevelNonFunction(); 2275 } 2276 2277 Iterator operator++() { 2278 next(); 2279 assertFunction(); 2280 return *this; 2281 } 2282 2283 void next() { 2284 MOZ_ASSERT(index_ < stencil_.scriptData.size()); 2285 index_++; 2286 } 2287 2288 void assertFunction() { 2289 if (index_ < stencil_.scriptData.size()) { 2290 MOZ_ASSERT(stencil_.scriptData[index_].isFunction()); 2291 } 2292 } 2293 2294 void skipTopLevelNonFunction() { 2295 MOZ_ASSERT(index_ == 0); 2296 if (stencil_.scriptData.size()) { 2297 if (!stencil_.scriptData[0].isFunction()) { 2298 next(); 2299 assertFunction(); 2300 } 2301 } 2302 } 2303 2304 bool operator!=(const Iterator& other) const { 2305 return index_ != other.index_; 2306 } 2307 2308 ScriptAndFunction operator*() { 2309 ScriptIndex index = ScriptIndex(index_); 2310 const ScriptStencil& script = stencil_.scriptData[index]; 2311 const ScriptStencilExtra* scriptExtra = nullptr; 2312 if (stencil_.isInitialStencil()) { 2313 scriptExtra = &stencil_.scriptExtra[index]; 2314 } 2315 return ScriptAndFunction(script, scriptExtra, 2316 gcOutput_.getFunctionNoBaseIndex(index), index); 2317 } 2318 2319 static Iterator end(const CompilationStencil& stencil, 2320 CompilationGCOutput& gcOutput) { 2321 return Iterator(stencil, gcOutput, stencil.scriptData.size()); 2322 } 2323 }; 2324 2325 const CompilationStencil& stencil_; 2326 CompilationGCOutput& gcOutput_; 2327 2328 explicit ScriptStencilIterable(const CompilationStencil& stencil, 2329 CompilationGCOutput& gcOutput) 2330 : stencil_(stencil), gcOutput_(gcOutput) {} 2331 2332 Iterator begin() const { return Iterator(stencil_, gcOutput_); } 2333 2334 Iterator end() const { return Iterator::end(stencil_, gcOutput_); } 2335 }; 2336 2337 inline ScriptStencilIterable CompilationStencil::functionScriptStencils( 2338 const CompilationStencil& stencil, CompilationGCOutput& gcOutput) { 2339 return ScriptStencilIterable(stencil, gcOutput); 2340 } 2341 2342 // Merge CompilationStencil for delazification into initial 2343 // ExtensibleCompilationStencil. 2344 struct CompilationStencilMerger { 2345 private: 2346 using FunctionKey = SourceExtent::FunctionKey; 2347 2348 // The stencil for the initial compilation. 2349 // Delazifications are merged into this. 2350 // 2351 // If any failure happens during merge operation, this field is reset to 2352 // nullptr. 2353 UniquePtr<ExtensibleCompilationStencil> initial_; 2354 2355 FunctionKeyToScriptIndexMap functionKeyToInitialScriptIndex_; 2356 2357 ScriptIndex getInitialScriptIndexFor( 2358 const CompilationStencil& delazification) const; 2359 2360 // A map from delazification's ParserAtomIndex to 2361 // initial's TaggedParserAtomIndex 2362 using AtomIndexMap = Vector<TaggedParserAtomIndex, 0, js::SystemAllocPolicy>; 2363 2364 [[nodiscard]] bool buildAtomIndexMap(FrontendContext* fc, 2365 const CompilationStencil& delazification, 2366 AtomIndexMap& atomIndexMap); 2367 2368 public: 2369 CompilationStencilMerger() = default; 2370 2371 // Set the initial stencil and prepare for merging. 2372 [[nodiscard]] bool setInitial( 2373 FrontendContext* fc, UniquePtr<ExtensibleCompilationStencil>&& initial); 2374 2375 // Merge the delazification stencil into the initial stencil. 2376 [[nodiscard]] bool addDelazification( 2377 FrontendContext* fc, const CompilationStencil& delazification); 2378 2379 // Merge the delazification stencil into the initial stencil if the 2380 // delazification stencil can be merged. 2381 // 2382 // If the delazification's enclosing function is not yet merged, this does 2383 // do nothing. 2384 [[nodiscard]] bool maybeAddDelazification( 2385 FrontendContext* fc, const CompilationStencil& delazification); 2386 2387 ExtensibleCompilationStencil& getResult() const { return *initial_; } 2388 UniquePtr<ExtensibleCompilationStencil> takeResult() { 2389 return std::move(initial_); 2390 } 2391 }; 2392 2393 ScriptStencilRef ScopeStencilRef::script() const { 2394 return ScriptStencilRef{stencils_, scriptIndex_}; 2395 } 2396 2397 const CompilationStencil* ScopeStencilRef::context() const { 2398 return script().context(); 2399 } 2400 2401 const ScopeStencil& ScopeStencilRef::scope() const { 2402 return context()->scopeData[scopeIndex_]; 2403 } 2404 2405 const ScriptStencilExtra& ScopeStencilRef::functionScriptExtra() const { 2406 MOZ_ASSERT(scope().isFunction()); 2407 // Extract the `ScriptIndex` from the function's scope. This index is valid in 2408 // the `CompilationStencil` which has the shared data for `scriptIndex_`. 2409 ScriptIndex functionIndexInContext = scope().functionIndex(); 2410 // Convert the function's index to an index in the initial stencil. 2411 ScriptIndex functionIndexInInitial = 2412 stencils_.getInitialIndexFor(scriptIndex_, functionIndexInContext); 2413 // Create a ScriptStencilRef from the function index in the initial stencil. 2414 ScriptStencilRef function{stencils_, functionIndexInInitial}; 2415 return function.scriptExtra(); 2416 } 2417 2418 InputScope InputScope::enclosing() const { 2419 return scope_.match( 2420 [](const Scope* ptr) { 2421 // This may return a nullptr Scope pointer. 2422 return InputScope(ptr->enclosing()); 2423 }, 2424 [](const ScopeStencilRef& ref) { 2425 auto& scope = ref.scope(); 2426 if (scope.hasEnclosing()) { 2427 #ifdef DEBUG 2428 // Assert that checking for the same stencil is equivalent to 2429 // checking for being encoded in the initial stencil. 2430 if (ref.scriptIndex_ != 0) { 2431 auto enclosingScript = ref.script().enclosingScript(); 2432 bool same = ref.context() == enclosingScript.context(); 2433 MOZ_ASSERT(same == ref.script().isEagerlyCompiledInInitial()); 2434 } 2435 #endif 2436 2437 // By default we are walking the scope within the same function. 2438 ScriptIndex scriptIndex = ref.scriptIndex_; 2439 2440 // `scope.enclosing()` and `scope` would have the same scriptIndex 2441 // unless `scope` is the first scope of the script. In which case, the 2442 // returned enclosing scope index should be returned with the 2443 // enclosing script index. 2444 // 2445 // This can only happen in the initial stencil, as only the initial 2446 // stencil can have multiple scripts compiled in the same stencil. 2447 if (ref.script().isEagerlyCompiledInInitial()) { 2448 auto gcThingsFromContext = ref.script().gcThingsFromInitial(); 2449 if (gcThingsFromContext[0].toScope() == ref.scopeIndex_) { 2450 scriptIndex = ref.script().enclosingScript().scriptIndex_; 2451 } 2452 } 2453 2454 return InputScope(ref.stencils_, scriptIndex, scope.enclosing()); 2455 } 2456 2457 // By default the previous condition (scope.hasEnclosing()) should 2458 // trigger, except when we are at the top-level of a delazification, in 2459 // which case we have to find the enclosing script in the stencil of the 2460 // enclosing script, to find the lazyFunctionEnclosingScopeIndex which 2461 // is valid in the stencil of the enclosing script. 2462 // 2463 // Note, at one point the enclosing script would be the initial stencil. 2464 if (!ref.script().isEagerlyCompiledInInitial()) { 2465 auto enclosing = ref.script().enclosingScript(); 2466 auto& scriptData = ref.script().scriptDataFromEnclosing(); 2467 MOZ_ASSERT(scriptData.hasLazyFunctionEnclosingScopeIndex()); 2468 return InputScope(ref.stencils_, enclosing.scriptIndex_, 2469 scriptData.lazyFunctionEnclosingScopeIndex()); 2470 } 2471 2472 // The global scope is not known by the Stencil, while parsing inner 2473 // functions from Stencils where they are known at the execution using 2474 // the GlobalScope. 2475 if (ref.scope().kind() == ScopeKind::Module) { 2476 return InputScope(FakeStencilGlobalScope{}); 2477 } 2478 return InputScope(nullptr); 2479 }, 2480 [](const FakeStencilGlobalScope&) { return InputScope(nullptr); }); 2481 } 2482 2483 FunctionFlags InputScope::functionFlags() const { 2484 return scope_.match( 2485 [](const Scope* ptr) { 2486 JSFunction* fun = ptr->as<FunctionScope>().canonicalFunction(); 2487 return fun->flags(); 2488 }, 2489 [](const ScopeStencilRef& ref) { 2490 MOZ_ASSERT(ref.scope().isFunction()); 2491 ScriptIndex functionIndexInContext = ref.scope().functionIndex(); 2492 // Unlike InputScript::functionFlags(), which returns the functionFlags 2493 // using the ScriptStencilRef::scriptDataFromEnclosing() function, 2494 // ref.context() is already the CompilationStencil holding the 2495 // information about the extracted function index. Using the same code 2496 // as in InputScript::functionFlags() would yield an error for cases 2497 // where the functionIndexInContext is 0, as we will look for the 2498 // scriptData in the wrong CompilationStencil. 2499 ScriptStencil& data = ref.context()->scriptData[functionIndexInContext]; 2500 return data.functionFlags; 2501 }, 2502 [](const FakeStencilGlobalScope&) -> FunctionFlags { 2503 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No functionFlags on global."); 2504 }); 2505 } 2506 2507 ImmutableScriptFlags InputScope::immutableFlags() const { 2508 return scope_.match( 2509 [](const Scope* ptr) { 2510 JSFunction* fun = ptr->as<FunctionScope>().canonicalFunction(); 2511 return fun->baseScript()->immutableFlags(); 2512 }, 2513 [](const ScopeStencilRef& ref) { 2514 return ref.functionScriptExtra().immutableFlags; 2515 }, 2516 [](const FakeStencilGlobalScope&) -> ImmutableScriptFlags { 2517 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No immutableFlags on global."); 2518 }); 2519 } 2520 2521 MemberInitializers InputScope::getMemberInitializers() const { 2522 return scope_.match( 2523 [](const Scope* ptr) { 2524 JSFunction* fun = ptr->as<FunctionScope>().canonicalFunction(); 2525 return fun->baseScript()->getMemberInitializers(); 2526 }, 2527 [](const ScopeStencilRef& ref) { 2528 return ref.functionScriptExtra().memberInitializers(); 2529 }, 2530 [](const FakeStencilGlobalScope&) -> MemberInitializers { 2531 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE( 2532 "No getMemberInitializers on global."); 2533 }); 2534 } 2535 2536 ScriptStencilRef ScriptStencilRef::topLevelScript() const { 2537 return ScriptStencilRef{stencils_, ScriptIndex(0)}; 2538 } 2539 2540 ScriptStencilRef ScriptStencilRef::enclosingScript() const { 2541 auto indexes = stencils_.getRelativeIndexesAt(scriptIndex_); 2542 ScriptStencilRef enclosing{stencils_, indexes.enclosingIndexInInitial}; 2543 return enclosing; 2544 } 2545 2546 const ScriptStencil& ScriptStencilRef::scriptDataFromInitial() const { 2547 return stencils_.getInitial()->scriptData[scriptIndex_]; 2548 } 2549 2550 bool ScriptStencilRef::isEagerlyCompiledInInitial() const { 2551 return scriptDataFromInitial().hasSharedData(); 2552 } 2553 2554 const ScriptStencil& ScriptStencilRef::scriptDataFromEnclosing() const { 2555 // The script data is held by the enclosing script except for the top-level. 2556 if (scriptIndex_ == 0) { 2557 return stencils_.getInitial()->scriptData[0]; 2558 } 2559 // Get the enclosing stencil. 2560 auto indexes = stencils_.getRelativeIndexesAt(scriptIndex_); 2561 ScriptStencilRef enclosing{stencils_, indexes.enclosingIndexInInitial}; 2562 return enclosing.context()->scriptData[indexes.indexInEnclosing]; 2563 } 2564 2565 mozilla::Span<TaggedScriptThingIndex> ScriptStencilRef::gcThingsFromInitial() 2566 const { 2567 return scriptDataFromInitial().gcthings(*stencils_.getInitial()); 2568 } 2569 2570 const ScriptStencilExtra& ScriptStencilRef::scriptExtra() const { 2571 return stencils_.getInitial()->scriptExtra[scriptIndex_]; 2572 } 2573 2574 const CompilationStencil* ScriptStencilRef::context() const { 2575 // The initial stencil might contain more than the top-level script, in which 2576 // case we should return the initial stencil when it contains the bytecode for 2577 // the script at the given index. 2578 if (isEagerlyCompiledInInitial()) { 2579 return stencils_.getInitial(); 2580 } 2581 const auto* delazification = stencils_.getDelazificationAt(scriptIndex_); 2582 MOZ_ASSERT(delazification); 2583 return delazification; 2584 } 2585 2586 const CompilationStencil* ScriptStencilRef::maybeContext() const { 2587 // The initial stencil might contain more than the top-level script, in which 2588 // case we should return the initial stencil when it contains the bytecode for 2589 // the script at the given index. 2590 if (isEagerlyCompiledInInitial()) { 2591 return stencils_.getInitial(); 2592 } 2593 return stencils_.getDelazificationAt(scriptIndex_); 2594 } 2595 2596 } // namespace frontend 2597 } // namespace js 2598 2599 namespace mozilla { 2600 template <> 2601 struct RefPtrTraits<js::frontend::CompilationStencil> { 2602 static void AddRef(js::frontend::CompilationStencil* stencil) { 2603 stencil->AddRef(); 2604 } 2605 static void Release(js::frontend::CompilationStencil* stencil) { 2606 stencil->Release(); 2607 } 2608 }; 2609 } // namespace mozilla 2610 2611 #endif // frontend_CompilationStencil_h