JSScript.h (85174B)
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 /* JS script descriptor. */ 8 9 #ifndef vm_JSScript_h 10 #define vm_JSScript_h 11 12 #include "mozilla/Atomics.h" 13 #include "mozilla/Maybe.h" 14 #include "mozilla/MaybeOneOf.h" 15 #include "mozilla/MemoryReporting.h" 16 #include "mozilla/RefPtr.h" 17 #include "mozilla/Span.h" 18 19 #include "mozilla/UniquePtr.h" 20 #include "mozilla/Utf8.h" 21 #include "mozilla/Variant.h" 22 23 #include <type_traits> // std::is_same 24 #include <utility> // std::move 25 26 #include "jstypes.h" 27 28 #include "frontend/ScriptIndex.h" // ScriptIndex 29 #include "gc/Barrier.h" 30 #include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin, JS::LimitedColumnNumberOneOrigin 31 #include "js/CompileOptions.h" 32 #include "js/Transcoding.h" 33 #include "js/UbiNode.h" 34 #include "js/UniquePtr.h" 35 #include "js/Utility.h" 36 #include "util/TrailingArray.h" 37 #include "vm/BytecodeIterator.h" 38 #include "vm/BytecodeLocation.h" 39 #include "vm/BytecodeUtil.h" 40 #include "vm/MutexIDs.h" // mutexid 41 #include "vm/NativeObject.h" 42 #include "vm/SharedImmutableStringsCache.h" 43 #include "vm/SharedStencil.h" // js::GCThingIndex, js::SourceExtent, js::SharedImmutableScriptData, MemberInitializers 44 #include "vm/StencilEnums.h" // SourceRetrievable 45 46 namespace JS { 47 struct ScriptSourceInfo; 48 template <typename UnitT> 49 class SourceText; 50 } // namespace JS 51 52 namespace js { 53 54 class Compressor; 55 class FrontendContext; 56 class ScriptSource; 57 58 class VarScope; 59 class LexicalScope; 60 61 class JS_PUBLIC_API Sprinter; 62 63 namespace coverage { 64 class LCovSource; 65 } // namespace coverage 66 67 namespace gc { 68 class AllocSite; 69 } // namespace gc 70 71 namespace jit { 72 class AutoKeepJitScripts; 73 class BaselineScript; 74 class IonScript; 75 struct IonScriptCounts; 76 class JitScript; 77 } // namespace jit 78 79 class ModuleObject; 80 class RegExpObject; 81 class SourceCompressionTaskEntry; 82 class Shape; 83 class SrcNote; 84 class DebugScript; 85 86 namespace frontend { 87 struct CompilationStencil; 88 struct ExtensibleCompilationStencil; 89 struct CompilationGCOutput; 90 struct InitialStencilAndDelazifications; 91 struct CompilationStencilMerger; 92 class StencilXDR; 93 } // namespace frontend 94 95 class ScriptCounts { 96 public: 97 using PCCountsVector = mozilla::Vector<PCCounts, 0, SystemAllocPolicy>; 98 99 inline ScriptCounts(); 100 inline explicit ScriptCounts(PCCountsVector&& jumpTargets); 101 inline ScriptCounts(ScriptCounts&& src); 102 inline ~ScriptCounts(); 103 104 inline ScriptCounts& operator=(ScriptCounts&& src); 105 106 // Return the counter used to count the number of visits. Returns null if 107 // the element is not found. 108 PCCounts* maybeGetPCCounts(size_t offset); 109 const PCCounts* maybeGetPCCounts(size_t offset) const; 110 111 // PCCounts are stored at jump-target offsets. This function looks for the 112 // previous PCCount which is in the same basic block as the current offset. 113 PCCounts* getImmediatePrecedingPCCounts(size_t offset); 114 115 // Return the counter used to count the number of throws. Returns null if 116 // the element is not found. 117 const PCCounts* maybeGetThrowCounts(size_t offset) const; 118 119 // Throw counts are stored at the location of each throwing 120 // instruction. This function looks for the previous throw count. 121 // 122 // Note: if the offset of the returned count is higher than the offset of 123 // the immediate preceding PCCount, then this throw happened in the same 124 // basic block. 125 const PCCounts* getImmediatePrecedingThrowCounts(size_t offset) const; 126 127 // Return the counter used to count the number of throws. Allocate it if 128 // none exists yet. Returns null if the allocation failed. 129 PCCounts* getThrowCounts(size_t offset); 130 131 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); 132 133 bool traceWeak(JSTracer* trc) { return true; } 134 135 private: 136 friend class ::JSScript; 137 friend struct ScriptAndCounts; 138 139 // This sorted array is used to map an offset to the number of times a 140 // branch got visited. 141 PCCountsVector pcCounts_; 142 143 // This sorted vector is used to map an offset to the number of times an 144 // instruction throw. 145 PCCountsVector throwCounts_; 146 147 // Information about any Ion compilations for the script. 148 jit::IonScriptCounts* ionCounts_; 149 }; 150 151 // The key of these side-table hash maps are intentionally not traced GC 152 // references to JSScript. Instead, we use bare pointers and manually fix up 153 // when objects could have moved (see Zone::fixupScriptMapsAfterMovingGC) and 154 // remove when the realm is destroyed (see Zone::clearScriptCounts and 155 // Zone::clearScriptNames). They essentially behave as weak references, except 156 // that the references are not cleared early by the GC. They must be non-strong 157 // references because the tables are kept at the Zone level and otherwise the 158 // table keys would keep scripts alive, thus keeping Realms alive, beyond their 159 // expected lifetimes. However, We do not use actual weak references (e.g. as 160 // used by WeakMap tables provided in gc/WeakMap.h) because they would be 161 // collected before the calls to the JSScript::finalize function which are used 162 // to aggregate code coverage results on the realm. 163 // 164 // Note carefully, however, that there is an exceptional case for which we *do* 165 // want the JSScripts to be strong references (and thus traced): when the 166 // --dump-bytecode command line option or the PCCount JSFriend API is used, 167 // then the scripts for all counts must remain alive. See 168 // Zone::traceScriptTableRoots() for more details. 169 // 170 // TODO: Clean this up by either aggregating coverage results in some other 171 // way, or by tweaking sweep ordering. 172 using UniqueScriptCounts = js::UniquePtr<ScriptCounts>; 173 using ScriptCountsMap = 174 GCRekeyableHashMap<HeapPtr<BaseScript*>, UniqueScriptCounts, 175 DefaultHasher<HeapPtr<BaseScript*>>, SystemAllocPolicy>; 176 177 // The 'const char*' for the function name is a pointer within the LCovSource's 178 // LifoAlloc and will be discarded at the same time. 179 using ScriptLCovEntry = std::tuple<coverage::LCovSource*, const char*>; 180 using ScriptLCovMap = 181 GCRekeyableHashMap<HeapPtr<BaseScript*>, ScriptLCovEntry, 182 DefaultHasher<HeapPtr<BaseScript*>>, SystemAllocPolicy>; 183 184 #ifdef MOZ_VTUNE 185 using ScriptVTuneIdMap = 186 GCRekeyableHashMap<HeapPtr<BaseScript*>, uint32_t, 187 DefaultHasher<HeapPtr<BaseScript*>>, SystemAllocPolicy>; 188 #endif 189 #ifdef JS_CACHEIR_SPEW 190 using ScriptFinalWarmUpCountEntry = std::tuple<uint32_t, SharedImmutableString>; 191 using ScriptFinalWarmUpCountMap = 192 GCRekeyableHashMap<HeapPtr<BaseScript*>, ScriptFinalWarmUpCountEntry, 193 DefaultHasher<HeapPtr<BaseScript*>>, SystemAllocPolicy>; 194 #endif 195 196 struct ScriptSourceChunk { 197 ScriptSource* ss = nullptr; 198 uint32_t chunk = 0; 199 200 ScriptSourceChunk() = default; 201 202 ScriptSourceChunk(ScriptSource* ss, uint32_t chunk) : ss(ss), chunk(chunk) { 203 MOZ_ASSERT(valid()); 204 } 205 206 bool valid() const { return ss != nullptr; } 207 208 bool operator==(const ScriptSourceChunk& other) const { 209 return ss == other.ss && chunk == other.chunk; 210 } 211 }; 212 213 struct ScriptSourceChunkHasher { 214 using Lookup = ScriptSourceChunk; 215 216 static HashNumber hash(const ScriptSourceChunk& ssc) { 217 return mozilla::AddToHash(DefaultHasher<ScriptSource*>::hash(ssc.ss), 218 ssc.chunk); 219 } 220 static bool match(const ScriptSourceChunk& c1, const ScriptSourceChunk& c2) { 221 return c1 == c2; 222 } 223 }; 224 225 template <typename Unit> 226 using EntryUnits = mozilla::UniquePtr<Unit[], JS::FreePolicy>; 227 228 // The uncompressed source cache contains *either* UTF-8 source data *or* 229 // UTF-16 source data. ScriptSourceChunk implies a ScriptSource that 230 // contains either UTF-8 data or UTF-16 data, so the nature of the key to 231 // Map below indicates how each SourceData ought to be interpreted. 232 using SourceData = mozilla::UniquePtr<void, JS::FreePolicy>; 233 234 template <typename Unit> 235 inline SourceData ToSourceData(EntryUnits<Unit> chars) { 236 static_assert(std::is_same_v<SourceData::deleter_type, 237 typename EntryUnits<Unit>::deleter_type>, 238 "EntryUnits and SourceData must share the same deleter " 239 "type, that need not know the type of the data being freed, " 240 "for the upcast below to be safe"); 241 return SourceData(chars.release()); 242 } 243 244 class UncompressedSourceCache { 245 using Map = HashMap<ScriptSourceChunk, SourceData, ScriptSourceChunkHasher, 246 SystemAllocPolicy>; 247 248 public: 249 // Hold an entry in the source data cache and prevent it from being purged on 250 // GC. 251 class AutoHoldEntry { 252 UncompressedSourceCache* cache_ = nullptr; 253 ScriptSourceChunk sourceChunk_ = {}; 254 SourceData data_ = nullptr; 255 256 public: 257 explicit AutoHoldEntry() = default; 258 259 ~AutoHoldEntry() { 260 if (cache_) { 261 MOZ_ASSERT(sourceChunk_.valid()); 262 cache_->releaseEntry(*this); 263 } 264 } 265 266 template <typename Unit> 267 void holdUnits(EntryUnits<Unit> units) { 268 MOZ_ASSERT(!cache_); 269 MOZ_ASSERT(!sourceChunk_.valid()); 270 MOZ_ASSERT(!data_); 271 272 data_ = ToSourceData(std::move(units)); 273 } 274 275 private: 276 void holdEntry(UncompressedSourceCache* cache, 277 const ScriptSourceChunk& sourceChunk) { 278 // Initialise the holder for a specific cache and script source. 279 // This will hold on to the cached source chars in the event that 280 // the cache is purged. 281 MOZ_ASSERT(!cache_); 282 MOZ_ASSERT(!sourceChunk_.valid()); 283 MOZ_ASSERT(!data_); 284 285 cache_ = cache; 286 sourceChunk_ = sourceChunk; 287 } 288 289 void deferDelete(SourceData data) { 290 // Take ownership of source chars now the cache is being purged. Remove 291 // our reference to the ScriptSource which might soon be destroyed. 292 MOZ_ASSERT(cache_); 293 MOZ_ASSERT(sourceChunk_.valid()); 294 MOZ_ASSERT(!data_); 295 296 cache_ = nullptr; 297 sourceChunk_ = ScriptSourceChunk(); 298 299 data_ = std::move(data); 300 } 301 302 const ScriptSourceChunk& sourceChunk() const { return sourceChunk_; } 303 friend class UncompressedSourceCache; 304 }; 305 306 private: 307 UniquePtr<Map> map_ = nullptr; 308 AutoHoldEntry* holder_ = nullptr; 309 310 public: 311 UncompressedSourceCache() = default; 312 313 template <typename Unit> 314 const Unit* lookup(const ScriptSourceChunk& ssc, AutoHoldEntry& asp); 315 316 bool put(const ScriptSourceChunk& ssc, SourceData data, AutoHoldEntry& asp); 317 318 void purge(); 319 320 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); 321 322 private: 323 void holdEntry(AutoHoldEntry& holder, const ScriptSourceChunk& ssc); 324 void releaseEntry(AutoHoldEntry& holder); 325 }; 326 327 template <typename Unit> 328 struct SourceTypeTraits; 329 330 template <> 331 struct SourceTypeTraits<mozilla::Utf8Unit> { 332 using CharT = char; 333 using SharedImmutableString = js::SharedImmutableString; 334 335 static const mozilla::Utf8Unit* units(const SharedImmutableString& string) { 336 // Casting |char| data to |Utf8Unit| is safe because |Utf8Unit| 337 // contains a |char|. See the long comment in |Utf8Unit|'s definition. 338 return reinterpret_cast<const mozilla::Utf8Unit*>(string.chars()); 339 } 340 341 static char* toString(const mozilla::Utf8Unit* units) { 342 auto asUnsigned = 343 const_cast<unsigned char*>(mozilla::Utf8AsUnsignedChars(units)); 344 return reinterpret_cast<char*>(asUnsigned); 345 } 346 347 static UniqueChars toCacheable(EntryUnits<mozilla::Utf8Unit> str) { 348 // The cache only stores strings of |char| or |char16_t|, and right now 349 // it seems best not to gunk up the cache with |Utf8Unit| too. So 350 // cache |Utf8Unit| strings by interpreting them as |char| strings. 351 char* chars = toString(str.release()); 352 return UniqueChars(chars); 353 } 354 }; 355 356 template <> 357 struct SourceTypeTraits<char16_t> { 358 using CharT = char16_t; 359 using SharedImmutableString = js::SharedImmutableTwoByteString; 360 361 static const char16_t* units(const SharedImmutableString& string) { 362 return string.chars(); 363 } 364 365 static char16_t* toString(const char16_t* units) { 366 return const_cast<char16_t*>(units); 367 } 368 369 static UniqueTwoByteChars toCacheable(EntryUnits<char16_t> str) { 370 return UniqueTwoByteChars(std::move(str)); 371 } 372 }; 373 374 // Synchronously compress the source of |script|, for testing purposes. 375 [[nodiscard]] extern bool SynchronouslyCompressSource( 376 JSContext* cx, JS::Handle<BaseScript*> script); 377 378 // Variant return type for ScriptSource::substringChars to support both UTF-8 379 // and UTF-16. 380 using SubstringCharsResult = 381 mozilla::Variant<JS::UniqueChars, JS::UniqueTwoByteChars>; 382 383 // [SMDOC] ScriptSource 384 // 385 // This class abstracts over the source we used to compile from. The current 386 // representation may transition to different modes in order to save memory. 387 // Abstractly the source may be one of UTF-8 or UTF-16. The data itself may be 388 // unavailable, retrieveable-using-source-hook, compressed, or uncompressed. If 389 // source is retrieved or decompressed for use, we may update the ScriptSource 390 // to hold the result. 391 class ScriptSource { 392 // NOTE: While ScriptSources may be compressed off thread, they are only 393 // modified by the main thread, and all members are always safe to access 394 // on the main thread. 395 396 friend class PendingSourceCompressionEntry; 397 friend class SourceCompressionTaskEntry; 398 friend bool SynchronouslyCompressSource(JSContext* cx, 399 JS::Handle<BaseScript*> script); 400 401 friend class frontend::StencilXDR; 402 403 private: 404 // Common base class of the templated variants of PinnedUnits<T>. 405 class PinnedUnitsBase { 406 protected: 407 ScriptSource* source_; 408 409 explicit PinnedUnitsBase(ScriptSource* source) : source_(source) {} 410 411 void addReader(); 412 413 template <typename Unit> 414 void removeReader(); 415 }; 416 417 public: 418 // Any users that wish to manipulate the char buffer of the ScriptSource 419 // needs to do so via PinnedUnits for GC safety. A GC may compress 420 // ScriptSources. If the source were initially uncompressed, then any raw 421 // pointers to the char buffer would now point to the freed, uncompressed 422 // chars. This is analogous to Rooted. 423 template <typename Unit> 424 class PinnedUnits : public PinnedUnitsBase { 425 const Unit* units_; 426 427 public: 428 // If maybeCx is nullptr, compressed sources will still be decompressed but 429 // the result will not be cached. This allows off-main-thread use without 430 // a JSContext. 431 PinnedUnits(JSContext* maybeCx, ScriptSource* source, 432 UncompressedSourceCache::AutoHoldEntry& holder, size_t begin, 433 size_t len); 434 435 ~PinnedUnits(); 436 437 const Unit* get() const { return units_; } 438 439 const typename SourceTypeTraits<Unit>::CharT* asChars() const { 440 return SourceTypeTraits<Unit>::toString(get()); 441 } 442 }; 443 444 template <typename Unit> 445 class PinnedUnitsIfUncompressed : public PinnedUnitsBase { 446 const Unit* units_; 447 448 public: 449 PinnedUnitsIfUncompressed(ScriptSource* source, size_t begin, size_t len); 450 451 ~PinnedUnitsIfUncompressed(); 452 453 const Unit* get() const { return units_; } 454 455 const typename SourceTypeTraits<Unit>::CharT* asChars() const { 456 return SourceTypeTraits<Unit>::toString(get()); 457 } 458 }; 459 460 private: 461 // Missing source text that isn't retrievable using the source hook. (All 462 // ScriptSources initially begin in this state. Users that are compiling 463 // source text will overwrite |data| to store a different state.) 464 struct Missing {}; 465 466 // Source that can be retrieved using the registered source hook. |Unit| 467 // records the source type so that source-text coordinates in functions and 468 // scripts that depend on this |ScriptSource| are correct. 469 template <typename Unit> 470 struct Retrievable { 471 // The source hook and script URL required to retrieve source are stored 472 // elsewhere, so nothing is needed here. It'd be better hygiene to store 473 // something source-hook-like in each |ScriptSource| that needs it, but that 474 // requires reimagining a source-hook API that currently depends on source 475 // hooks being uniquely-owned pointers... 476 }; 477 478 // Uncompressed source text. Templates distinguish if we are interconvertable 479 // to |Retrievable| or not. 480 template <typename Unit> 481 class UncompressedData { 482 typename SourceTypeTraits<Unit>::SharedImmutableString string_; 483 484 public: 485 explicit UncompressedData( 486 typename SourceTypeTraits<Unit>::SharedImmutableString str) 487 : string_(std::move(str)) {} 488 489 const Unit* units() const { return SourceTypeTraits<Unit>::units(string_); } 490 491 size_t length() const { return string_.length(); } 492 }; 493 494 template <typename Unit, SourceRetrievable CanRetrieve> 495 class Uncompressed : public UncompressedData<Unit> { 496 using Base = UncompressedData<Unit>; 497 498 public: 499 using Base::Base; 500 }; 501 502 // Compressed source text. Templates distinguish if we are interconvertable 503 // to |Retrievable| or not. 504 template <typename Unit> 505 struct CompressedData { 506 // Single-byte compressed text, regardless whether the original text 507 // was single-byte or two-byte. 508 SharedImmutableString raw; 509 size_t uncompressedLength; 510 511 CompressedData(SharedImmutableString raw, size_t uncompressedLength) 512 : raw(std::move(raw)), uncompressedLength(uncompressedLength) {} 513 }; 514 515 template <typename Unit, SourceRetrievable CanRetrieve> 516 struct Compressed : public CompressedData<Unit> { 517 using Base = CompressedData<Unit>; 518 519 public: 520 using Base::Base; 521 }; 522 523 // The set of currently allowed encoding modes. 524 using SourceType = 525 mozilla::Variant<Compressed<mozilla::Utf8Unit, SourceRetrievable::Yes>, 526 Uncompressed<mozilla::Utf8Unit, SourceRetrievable::Yes>, 527 Compressed<mozilla::Utf8Unit, SourceRetrievable::No>, 528 Uncompressed<mozilla::Utf8Unit, SourceRetrievable::No>, 529 Compressed<char16_t, SourceRetrievable::Yes>, 530 Uncompressed<char16_t, SourceRetrievable::Yes>, 531 Compressed<char16_t, SourceRetrievable::No>, 532 Uncompressed<char16_t, SourceRetrievable::No>, 533 Retrievable<mozilla::Utf8Unit>, Retrievable<char16_t>, 534 Missing>; 535 536 // 537 // Start of fields. 538 // 539 540 mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refs = {}; 541 542 // An id for this source that is unique across the process. This can be used 543 // to refer to this source from places that don't want to hold a strong 544 // reference on the source itself. 545 // 546 // This is a 32 bit ID and could overflow, in which case the ID will not be 547 // unique anymore. 548 uint32_t id_ = 0; 549 550 // Source data (as a mozilla::Variant). 551 SourceType data = SourceType(Missing()); 552 553 // If the GC calls triggerConvertToCompressedSource with PinnedUnits present, 554 // the last PinnedUnits instance will install the compressed chars upon 555 // destruction. 556 // 557 // Retrievability isn't part of the type here because uncompressed->compressed 558 // transitions must preserve existing retrievability. 559 struct ReaderInstances { 560 size_t count = 0; 561 mozilla::MaybeOneOf<CompressedData<mozilla::Utf8Unit>, 562 CompressedData<char16_t>> 563 pendingCompressed; 564 }; 565 ExclusiveData<ReaderInstances> readers_; 566 567 // The UTF-8 encoded filename of this script. 568 SharedImmutableString filename_; 569 570 // Hash of the script filename; 571 HashNumber filenameHash_ = 0; 572 573 // If this ScriptSource was generated by a code-introduction mechanism such 574 // as |eval| or |new Function|, the debugger needs access to the "raw" 575 // filename of the top-level script that contains the eval-ing code. To 576 // keep track of this, we must preserve the original outermost filename (of 577 // the original introducer script), so that instead of a filename of 578 // "foo.js line 30 > eval line 10 > Function", we can obtain the original 579 // raw filename of "foo.js". 580 // 581 // In the case described above, this field will be set to to the original raw 582 // UTF-8 encoded filename from above, otherwise it will be mozilla::Nothing. 583 SharedImmutableString introducerFilename_; 584 585 SharedImmutableTwoByteString displayURL_; 586 SharedImmutableTwoByteString sourceMapURL_; 587 588 // A string indicating how this source code was introduced into the system. 589 // This is a constant, statically allocated C string, so does not need memory 590 // management. 591 // 592 // TODO: Document the various additional introduction type constants. 593 const char* introductionType_ = nullptr; 594 595 // Bytecode offset in caller script that generated this code. This is 596 // present for eval-ed code, as well as "new Function(...)"-introduced 597 // scripts. 598 mozilla::Maybe<uint32_t> introductionOffset_; 599 600 // If this source is for Function constructor, the position of ")" after 601 // parameter list in the source. This is used to get function body. 602 // 0 for other cases. 603 uint32_t parameterListEnd_ = 0; 604 605 // Line number within the file where this source starts (1-origin). 606 uint32_t startLine_ = 0; 607 // Column number within the file where this source starts, 608 // in UTF-16 code units. 609 JS::LimitedColumnNumberOneOrigin startColumn_; 610 611 // See: CompileOptions::mutedErrors. 612 bool mutedErrors_ = false; 613 614 // Carry the delazification mode per source. 615 JS::DelazificationOption delazificationMode_ = 616 JS::DelazificationOption::OnDemandOnly; 617 618 // True if an associated SourceCompressionTask was ever created. 619 bool hadCompressionTask_ = false; 620 621 // 622 // End of fields. 623 // 624 625 // How many ids have been handed out to sources. 626 static mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent> idCount_; 627 628 // Decompress and return the specified chunk of source code. 629 // If maybeCx is nullptr, decompression still works but the uncompressed 630 // result will not be cached. This allows off-main-thread callers to 631 // decompress source without a JSContext, at the cost of potentially 632 // decompressing the same chunk multiple times. 633 template <typename Unit> 634 const Unit* chunkUnits(JSContext* maybeCx, 635 UncompressedSourceCache::AutoHoldEntry& holder, 636 size_t chunk); 637 638 // Return a string containing the chars starting at |begin| and ending at 639 // |begin + len|. 640 // 641 // Warning: this is *not* GC-safe! Any chars to be handed out must use 642 // PinnedUnits. See comment below. 643 // 644 // If maybeCx is nullptr, compressed sources will still be decompressed but 645 // the result will not be cached. See chunkUnits comment above. 646 template <typename Unit> 647 const Unit* units(JSContext* maybeCx, 648 UncompressedSourceCache::AutoHoldEntry& asp, size_t begin, 649 size_t len); 650 651 template <typename Unit> 652 const Unit* uncompressedUnits(size_t begin, size_t len); 653 654 public: 655 // When creating a JSString* from TwoByte source characters, we don't try to 656 // to deflate to Latin1 for longer strings, because this can be slow. 657 static const size_t SourceDeflateLimit = 100; 658 659 explicit ScriptSource() 660 : id_(++idCount_), readers_(js::mutexid::SourceCompression) {} 661 ~ScriptSource() { MOZ_ASSERT(refs == 0); } 662 663 void AddRef() { refs++; } 664 void Release() { 665 MOZ_ASSERT(refs != 0); 666 if (--refs == 0) { 667 js_delete(this); 668 } 669 } 670 [[nodiscard]] bool initFromOptions(FrontendContext* fc, 671 const JS::ReadOnlyCompileOptions& options); 672 673 /** 674 * The minimum script length (in code units) necessary for a script to be 675 * eligible to be compressed. 676 */ 677 static constexpr size_t MinimumCompressibleLength = 256; 678 679 SharedImmutableString getOrCreateStringZ(FrontendContext* fc, 680 UniqueChars&& str); 681 SharedImmutableTwoByteString getOrCreateStringZ(FrontendContext* fc, 682 UniqueTwoByteChars&& str); 683 684 private: 685 class LoadSourceMatcherBase; 686 class LoadSourceMatcher; 687 class SourcePropertiesGetter; 688 689 public: 690 // Attempt to load usable source for |ss| -- source text on which substring 691 // operations and the like can be performed. On success return true and set 692 // |*loaded| to indicate whether usable source could be loaded; otherwise 693 // return false. 694 static bool loadSource(JSContext* cx, ScriptSource* ss, bool* loaded); 695 696 // This is similar to loadSource, but it is designed to be used outside of the 697 // main thread. This is done by removing the need of JSContext for the 698 // Retrievable sources that require sourceHook. For retrievable cases, it 699 // sets retrievable to true and sets the isUT16 depending on the encoding. 700 // 701 // *loaded indicates whether source text is available, *retrievable indicates 702 // whether the source can be retrieved later via source hook. 703 static void getSourceProperties(ScriptSource* ss, bool* hasSourceText, 704 bool* retrievable); 705 706 // Assign source data from |srcBuf| to this recently-created |ScriptSource|. 707 template <typename Unit> 708 [[nodiscard]] bool assignSource(FrontendContext* fc, 709 const JS::ReadOnlyCompileOptions& options, 710 JS::SourceText<Unit>& srcBuf); 711 712 bool hasSourceText() const { 713 return hasUncompressedSource() || hasCompressedSource(); 714 } 715 716 private: 717 template <typename Unit> 718 struct UncompressedDataMatcher { 719 template <SourceRetrievable CanRetrieve> 720 const UncompressedData<Unit>* operator()( 721 const Uncompressed<Unit, CanRetrieve>& u) { 722 return &u; 723 } 724 725 template <typename T> 726 const UncompressedData<Unit>* operator()(const T&) { 727 MOZ_CRASH( 728 "attempting to access uncompressed data in a ScriptSource not " 729 "containing it"); 730 return nullptr; 731 } 732 }; 733 734 public: 735 template <typename Unit> 736 const UncompressedData<Unit>* uncompressedData() { 737 return data.match(UncompressedDataMatcher<Unit>()); 738 } 739 740 private: 741 template <typename Unit> 742 struct CompressedDataMatcher { 743 template <SourceRetrievable CanRetrieve> 744 const CompressedData<Unit>* operator()( 745 const Compressed<Unit, CanRetrieve>& c) { 746 return &c; 747 } 748 749 template <typename T> 750 const CompressedData<Unit>* operator()(const T&) { 751 MOZ_CRASH( 752 "attempting to access compressed data in a ScriptSource not " 753 "containing it"); 754 return nullptr; 755 } 756 }; 757 758 public: 759 template <typename Unit> 760 const CompressedData<Unit>* compressedData() { 761 return data.match(CompressedDataMatcher<Unit>()); 762 } 763 764 private: 765 struct HasUncompressedSource { 766 template <typename Unit, SourceRetrievable CanRetrieve> 767 bool operator()(const Uncompressed<Unit, CanRetrieve>&) { 768 return true; 769 } 770 771 template <typename Unit, SourceRetrievable CanRetrieve> 772 bool operator()(const Compressed<Unit, CanRetrieve>&) { 773 return false; 774 } 775 776 template <typename Unit> 777 bool operator()(const Retrievable<Unit>&) { 778 return false; 779 } 780 781 bool operator()(const Missing&) { return false; } 782 }; 783 784 public: 785 bool hasUncompressedSource() const { 786 return data.match(HasUncompressedSource()); 787 } 788 789 private: 790 template <typename Unit> 791 struct IsUncompressed { 792 template <SourceRetrievable CanRetrieve> 793 bool operator()(const Uncompressed<Unit, CanRetrieve>&) { 794 return true; 795 } 796 797 template <typename T> 798 bool operator()(const T&) { 799 return false; 800 } 801 }; 802 803 public: 804 template <typename Unit> 805 bool isUncompressed() const { 806 return data.match(IsUncompressed<Unit>()); 807 } 808 809 private: 810 struct HasCompressedSource { 811 template <typename Unit, SourceRetrievable CanRetrieve> 812 bool operator()(const Compressed<Unit, CanRetrieve>&) { 813 return true; 814 } 815 816 template <typename T> 817 bool operator()(const T&) { 818 return false; 819 } 820 }; 821 822 public: 823 bool hasCompressedSource() const { return data.match(HasCompressedSource()); } 824 825 private: 826 template <typename Unit> 827 struct IsCompressed { 828 template <SourceRetrievable CanRetrieve> 829 bool operator()(const Compressed<Unit, CanRetrieve>&) { 830 return true; 831 } 832 833 template <typename T> 834 bool operator()(const T&) { 835 return false; 836 } 837 }; 838 839 public: 840 template <typename Unit> 841 bool isCompressed() const { 842 return data.match(IsCompressed<Unit>()); 843 } 844 845 private: 846 template <typename Unit> 847 struct SourceTypeMatcher { 848 template <template <typename C, SourceRetrievable R> class Data, 849 SourceRetrievable CanRetrieve> 850 bool operator()(const Data<Unit, CanRetrieve>&) { 851 return true; 852 } 853 854 template <template <typename C, SourceRetrievable R> class Data, 855 typename NotUnit, SourceRetrievable CanRetrieve> 856 bool operator()(const Data<NotUnit, CanRetrieve>&) { 857 return false; 858 } 859 860 bool operator()(const Retrievable<Unit>&) { 861 MOZ_CRASH("source type only applies where actual text is available"); 862 return false; 863 } 864 865 template <typename NotUnit> 866 bool operator()(const Retrievable<NotUnit>&) { 867 return false; 868 } 869 870 bool operator()(const Missing&) { 871 MOZ_CRASH("doesn't make sense to ask source type when missing"); 872 return false; 873 } 874 }; 875 876 public: 877 template <typename Unit> 878 bool hasSourceType() const { 879 return data.match(SourceTypeMatcher<Unit>()); 880 } 881 882 private: 883 struct UncompressedLengthMatcher { 884 template <typename Unit, SourceRetrievable CanRetrieve> 885 size_t operator()(const Uncompressed<Unit, CanRetrieve>& u) { 886 return u.length(); 887 } 888 889 template <typename Unit, SourceRetrievable CanRetrieve> 890 size_t operator()(const Compressed<Unit, CanRetrieve>& u) { 891 return u.uncompressedLength; 892 } 893 894 template <typename Unit> 895 size_t operator()(const Retrievable<Unit>&) { 896 MOZ_CRASH("ScriptSource::length on a missing-but-retrievable source"); 897 return 0; 898 } 899 900 size_t operator()(const Missing& m) { 901 MOZ_CRASH("ScriptSource::length on a missing source"); 902 return 0; 903 } 904 }; 905 906 public: 907 size_t length() const { 908 MOZ_ASSERT(hasSourceText()); 909 return data.match(UncompressedLengthMatcher()); 910 } 911 912 JSLinearString* substring(JSContext* cx, size_t start, size_t stop); 913 JSLinearString* substringDontDeflate(JSContext* cx, size_t start, 914 size_t stop); 915 // Get substring characters without creating a JSString. Returns a variant 916 // containing either UniqueChars (UTF-8) or UniqueTwoByteChars (UTF-16). 917 // 918 // IMPORTANT: The returned buffer is NOT null-terminated. Callers must track 919 // the length separately (stop - start). This is designed for consumers that 920 // store length explicitly (e.g., ProfilerJSSourceData). 921 // 922 // Callers must handle empty sources before calling this (the function asserts 923 // non-empty length). Returns nullptr only on allocation failures. Designed 924 // for off-main-thread use where JSContext is not available for error 925 // reporting. 926 SubstringCharsResult substringChars(size_t start, size_t stop); 927 928 [[nodiscard]] bool appendSubstring(JSContext* cx, js::StringBuilder& buf, 929 size_t start, size_t stop); 930 931 void setParameterListEnd(uint32_t parameterListEnd) { 932 parameterListEnd_ = parameterListEnd; 933 } 934 935 bool isFunctionBody() const { return parameterListEnd_ != 0; } 936 JSLinearString* functionBodyString(JSContext* cx); 937 938 // Returns the function body substring. Unlike substringChars, this can return 939 // an empty result (nullptr with *outLength == 0) for empty function bodies. 940 // The caller doesn't need to check the length before calling. 941 SubstringCharsResult functionBodyStringChars(size_t* outLength); 942 943 // Returns true if this source should display only the function body. 944 // In case of DOM event handler like <div onclick="foo()" the JS code is 945 // wrapped into 946 // function onclick() {foo()} 947 // We want to only return `foo()` here. 948 // But only for event handlers, for `new Function("foo()")`, we want to 949 // return: 950 // function anonymous() {foo()} 951 bool shouldUnwrapEventHandlerBody() const { 952 return hasIntroductionType() && 953 strcmp(introductionType(), "eventHandler") == 0 && isFunctionBody(); 954 } 955 956 void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, 957 JS::ScriptSourceInfo* info) const; 958 959 private: 960 // Overwrites |data| with the uncompressed data from |source|. 961 // 962 // This function asserts nothing about |data|. Users should use assertions to 963 // double-check their own understandings of the |data| state transition being 964 // performed. 965 template <typename ContextT, typename Unit> 966 [[nodiscard]] bool setUncompressedSourceHelper(ContextT* cx, 967 EntryUnits<Unit>&& source, 968 size_t length, 969 SourceRetrievable retrievable); 970 971 public: 972 // Initialize a fresh |ScriptSource| with unretrievable, uncompressed source. 973 template <typename Unit> 974 [[nodiscard]] bool initializeUnretrievableUncompressedSource( 975 FrontendContext* fc, EntryUnits<Unit>&& source, size_t length); 976 977 // Set the retrieved source for a |ScriptSource| whose source was recorded as 978 // missing but retrievable. 979 template <typename Unit> 980 [[nodiscard]] bool setRetrievedSource(JSContext* cx, 981 EntryUnits<Unit>&& source, 982 size_t length); 983 984 [[nodiscard]] bool tryCompressOffThread(JSContext* cx); 985 986 // Called by the SourceCompressionTask constructor to indicate such a task was 987 // ever created. 988 void noteSourceCompressionTask() { hadCompressionTask_ = true; } 989 990 // *Trigger* the conversion of this ScriptSource from containing uncompressed 991 // |Unit|-encoded source to containing compressed source. Conversion may not 992 // be complete when this function returns: it'll be delayed if there's ongoing 993 // use of the uncompressed source via |PinnedUnits|, in which case conversion 994 // won't occur until the outermost |PinnedUnits| is destroyed. 995 // 996 // Compressed source is in bytes, no matter that |Unit| might be |char16_t|. 997 // |sourceLength| is the length in code units (not bytes) of the uncompressed 998 // source. 999 template <typename Unit> 1000 void triggerConvertToCompressedSource(SharedImmutableString compressed, 1001 size_t sourceLength); 1002 1003 // Initialize a fresh ScriptSource as containing unretrievable compressed 1004 // source of the indicated original encoding. 1005 template <typename Unit> 1006 [[nodiscard]] bool initializeWithUnretrievableCompressedSource( 1007 FrontendContext* fc, UniqueChars&& raw, size_t rawLength, 1008 size_t sourceLength); 1009 1010 private: 1011 void performTaskWork(SourceCompressionTaskEntry* task, Compressor& comp); 1012 1013 struct TriggerConvertToCompressedSourceFromTask { 1014 ScriptSource* const source_; 1015 SharedImmutableString& compressed_; 1016 1017 TriggerConvertToCompressedSourceFromTask(ScriptSource* source, 1018 SharedImmutableString& compressed) 1019 : source_(source), compressed_(compressed) {} 1020 1021 template <typename Unit, SourceRetrievable CanRetrieve> 1022 void operator()(const Uncompressed<Unit, CanRetrieve>&) { 1023 source_->triggerConvertToCompressedSource<Unit>(std::move(compressed_), 1024 source_->length()); 1025 } 1026 1027 template <typename Unit, SourceRetrievable CanRetrieve> 1028 void operator()(const Compressed<Unit, CanRetrieve>&) { 1029 MOZ_CRASH( 1030 "can't set compressed source when source is already compressed -- " 1031 "ScriptSource::tryCompressOffThread shouldn't have queued up this " 1032 "task?"); 1033 } 1034 1035 template <typename Unit> 1036 void operator()(const Retrievable<Unit>&) { 1037 MOZ_CRASH("shouldn't compressing unloaded-but-retrievable source"); 1038 } 1039 1040 void operator()(const Missing&) { 1041 MOZ_CRASH( 1042 "doesn't make sense to set compressed source for missing source -- " 1043 "ScriptSource::tryCompressOffThread shouldn't have queued up this " 1044 "task?"); 1045 } 1046 }; 1047 1048 template <typename Unit> 1049 void convertToCompressedSource(SharedImmutableString compressed, 1050 size_t uncompressedLength); 1051 1052 template <typename Unit> 1053 void performDelayedConvertToCompressedSource( 1054 ExclusiveData<ReaderInstances>::Guard& g); 1055 1056 void triggerConvertToCompressedSourceFromTask( 1057 SharedImmutableString compressed); 1058 1059 public: 1060 HashNumber filenameHash() const { return filenameHash_; } 1061 const char* filename() const { 1062 return filename_ ? filename_.chars() : nullptr; 1063 } 1064 [[nodiscard]] bool setFilename(FrontendContext* fc, const char* filename); 1065 [[nodiscard]] bool setFilename(FrontendContext* fc, UniqueChars&& filename); 1066 1067 bool hasIntroducerFilename() const { 1068 return introducerFilename_ ? true : false; 1069 } 1070 const char* introducerFilename() const { 1071 return introducerFilename_ ? introducerFilename_.chars() : filename(); 1072 } 1073 [[nodiscard]] bool setIntroducerFilename(FrontendContext* fc, 1074 const char* filename); 1075 [[nodiscard]] bool setIntroducerFilename(FrontendContext* fc, 1076 UniqueChars&& filename); 1077 1078 bool hasIntroductionType() const { return introductionType_; } 1079 const char* introductionType() const { 1080 MOZ_ASSERT(hasIntroductionType()); 1081 return introductionType_; 1082 } 1083 1084 uint32_t id() const { return id_; } 1085 1086 // Display URLs 1087 [[nodiscard]] bool setDisplayURL(FrontendContext* fc, const char16_t* url); 1088 [[nodiscard]] bool setDisplayURL(FrontendContext* fc, 1089 UniqueTwoByteChars&& url); 1090 bool hasDisplayURL() const { return bool(displayURL_); } 1091 const char16_t* displayURL() { return displayURL_.chars(); } 1092 1093 // Source maps 1094 [[nodiscard]] bool setSourceMapURL(FrontendContext* fc, const char16_t* url); 1095 [[nodiscard]] bool setSourceMapURL(FrontendContext* fc, 1096 UniqueTwoByteChars&& url); 1097 bool hasSourceMapURL() const { return bool(sourceMapURL_); } 1098 const char16_t* sourceMapURL() { return sourceMapURL_.chars(); } 1099 1100 bool mutedErrors() const { return mutedErrors_; } 1101 1102 uint32_t startLine() const { return startLine_; } 1103 JS::LimitedColumnNumberOneOrigin startColumn() const { return startColumn_; } 1104 1105 JS::DelazificationOption delazificationMode() const { 1106 return delazificationMode_; 1107 } 1108 1109 bool hasIntroductionOffset() const { return introductionOffset_.isSome(); } 1110 uint32_t introductionOffset() const { return introductionOffset_.value(); } 1111 void setIntroductionOffset(uint32_t offset) { 1112 MOZ_ASSERT(!hasIntroductionOffset()); 1113 MOZ_ASSERT(offset <= (uint32_t)INT32_MAX); 1114 introductionOffset_.emplace(offset); 1115 } 1116 }; 1117 1118 // [SMDOC] ScriptSourceObject 1119 // 1120 // ScriptSourceObject stores the ScriptSource and GC pointers related to it. 1121 class ScriptSourceObject : public NativeObject { 1122 static const JSClassOps classOps_; 1123 1124 public: 1125 static const JSClass class_; 1126 1127 static void finalize(JS::GCContext* gcx, JSObject* obj); 1128 1129 static ScriptSourceObject* create(JSContext* cx, ScriptSource* source); 1130 1131 // Initialize those properties of this ScriptSourceObject whose values 1132 // are provided by |options|, re-wrapping as necessary. 1133 static bool initFromOptions(JSContext* cx, 1134 JS::Handle<ScriptSourceObject*> source, 1135 const JS::InstantiateOptions& options); 1136 1137 static bool initElementProperties(JSContext* cx, 1138 JS::Handle<ScriptSourceObject*> source, 1139 HandleString elementAttrName); 1140 1141 bool hasSource() const { return !getReservedSlot(SOURCE_SLOT).isUndefined(); } 1142 ScriptSource* source() const { 1143 return static_cast<ScriptSource*>(getReservedSlot(SOURCE_SLOT).toPrivate()); 1144 } 1145 1146 JSObject* unwrappedElement(JSContext* cx) const; 1147 1148 const Value& unwrappedElementAttributeName() const { 1149 MOZ_ASSERT(isInitialized()); 1150 const Value& v = getReservedSlot(ELEMENT_PROPERTY_SLOT); 1151 MOZ_ASSERT(!v.isMagic()); 1152 return v; 1153 } 1154 BaseScript* unwrappedIntroductionScript() const { 1155 MOZ_ASSERT(isInitialized()); 1156 Value value = getReservedSlot(INTRODUCTION_SCRIPT_SLOT); 1157 if (value.isUndefined()) { 1158 return nullptr; 1159 } 1160 return value.toGCThing()->as<BaseScript>(); 1161 } 1162 1163 void setPrivate(JSRuntime* rt, const Value& value); 1164 void clearPrivate(JSRuntime* rt); 1165 1166 void setIntroductionScript(const Value& introductionScript) { 1167 setReservedSlot(INTRODUCTION_SCRIPT_SLOT, introductionScript); 1168 } 1169 1170 Value getPrivate() const { 1171 MOZ_ASSERT(isInitialized()); 1172 Value value = getReservedSlot(PRIVATE_SLOT); 1173 return value; 1174 } 1175 1176 private: 1177 #ifdef DEBUG 1178 bool isInitialized() const { 1179 Value element = getReservedSlot(ELEMENT_PROPERTY_SLOT); 1180 if (element.isMagic(JS_GENERIC_MAGIC)) { 1181 return false; 1182 } 1183 return !getReservedSlot(INTRODUCTION_SCRIPT_SLOT).isMagic(JS_GENERIC_MAGIC); 1184 } 1185 #endif 1186 1187 enum { 1188 PRIVATE_SLOT = 0, // Must be first slot for JSCLASS_SLOT0_IS_NSISUPPORTS. 1189 SOURCE_SLOT, 1190 ELEMENT_PROPERTY_SLOT, 1191 INTRODUCTION_SCRIPT_SLOT, 1192 STENCILS_SLOT, 1193 RESERVED_SLOTS 1194 }; 1195 1196 // Delazification stencils can be aggregated in 1197 // InitialStencilAndDelazification, this structure might be used for 1198 // different purposes. 1199 // - Collecting: The goal is to aggregate all delazified functions in order 1200 // to aggregate them for serialization. 1201 // - Sharing: The goal is to use the InitialStencilAndDelazification as a way 1202 // to share multiple threads efforts towards parsing a Script Source 1203 // content. 1204 // 1205 // See setCollectingDelazifications and setSharingDelazifications for details. 1206 static constexpr uintptr_t STENCILS_COLLECTING_DELAZIFICATIONS_FLAG = 0x1; 1207 static constexpr uintptr_t STENCILS_SHARING_DELAZIFICATIONS_FLAG = 0x2; 1208 static constexpr uintptr_t STENCILS_MASK = 0x3; 1209 1210 void clearStencils(); 1211 1212 template <uintptr_t flag> 1213 void setStencilsFlag(); 1214 1215 template <uintptr_t flag> 1216 void unsetStencilsFlag(); 1217 1218 template <uintptr_t flag> 1219 bool isStencilsFlagSet() const; 1220 1221 public: 1222 // Associate stencils to this ScriptSourceObject. 1223 // The consumer should call setCollectingDelazifications or 1224 // setSharingDelazifications after this. 1225 void setStencils( 1226 already_AddRefed<frontend::InitialStencilAndDelazifications> stencils); 1227 1228 // Start collecting delazifications. 1229 // This is a temporary state until unsetCollectingDelazifications is called, 1230 // and this expects a pair of set/unset call. 1231 // 1232 // The caller should check isCollectingDelazifications before calling this. 1233 void setCollectingDelazifications(); 1234 1235 // Clear the flag for collecting delazifications. 1236 // 1237 // If setSharingDelazifications wasn't called, this clears the association 1238 // with the stencils. 1239 void unsetCollectingDelazifications(); 1240 1241 // Returns true if setCollectingDelazifications was called and 1242 // unsetCollectingDelazifications is not yet called. 1243 bool isCollectingDelazifications() const; 1244 1245 // Start sharing delazifications with others. 1246 // This is a permanent state. 1247 // 1248 // The flag is orthogonal to setCollectingDelazifications. 1249 void setSharingDelazifications(); 1250 1251 // Returns true if setSharingDelazifications was called. 1252 bool isSharingDelazifications() const; 1253 1254 // Return the associated stencils if any. 1255 // Returns nullptr if stencils is not associated 1256 frontend::InitialStencilAndDelazifications* maybeGetStencils(); 1257 }; 1258 1259 // ScriptWarmUpData represents a pointer-sized field in BaseScript that stores 1260 // one of the following using low-bit tags: 1261 // 1262 // * The enclosing BaseScript. This is only used while this script is lazy and 1263 // its containing script is also lazy. This outer script must be compiled 1264 // before the current script can in order to correctly build the scope chain. 1265 // 1266 // * The enclosing Scope. This is only used while this script is lazy and its 1267 // containing script is compiled. This is the outer scope chain that will be 1268 // used to compile this scipt. 1269 // 1270 // * The script's warm-up count. This is only used until the script has a 1271 // JitScript. The Baseline Interpreter and JITs use the warm-up count stored 1272 // in JitScript. 1273 // 1274 // * A pointer to the JitScript, when the script is warm enough for the Baseline 1275 // Interpreter. 1276 // 1277 class ScriptWarmUpData { 1278 uintptr_t data_ = ResetState(); 1279 1280 private: 1281 static constexpr uintptr_t NumTagBits = 2; 1282 static constexpr uint32_t MaxWarmUpCount = UINT32_MAX >> NumTagBits; 1283 1284 public: 1285 // Public only for the JITs. 1286 static constexpr uintptr_t TagMask = (1 << NumTagBits) - 1; 1287 static constexpr uintptr_t JitScriptTag = 0; 1288 static constexpr uintptr_t EnclosingScriptTag = 1; 1289 static constexpr uintptr_t EnclosingScopeTag = 2; 1290 static constexpr uintptr_t WarmUpCountTag = 3; 1291 1292 private: 1293 // A gc-safe value to clear to. 1294 constexpr uintptr_t ResetState() { return 0 | WarmUpCountTag; } 1295 1296 template <uintptr_t Tag> 1297 inline void setTaggedPtr(void* ptr) { 1298 static_assert(Tag <= TagMask, "Tag must fit in TagMask"); 1299 MOZ_ASSERT((uintptr_t(ptr) & TagMask) == 0); 1300 data_ = uintptr_t(ptr) | Tag; 1301 } 1302 1303 template <typename T, uintptr_t Tag> 1304 inline T getTaggedPtr() const { 1305 static_assert(Tag <= TagMask, "Tag must fit in TagMask"); 1306 MOZ_ASSERT((data_ & TagMask) == Tag); 1307 return reinterpret_cast<T>(data_ & ~TagMask); 1308 } 1309 1310 void setWarmUpCount(uint32_t count) { 1311 if (count > MaxWarmUpCount) { 1312 count = MaxWarmUpCount; 1313 } 1314 data_ = (uintptr_t(count) << NumTagBits) | WarmUpCountTag; 1315 } 1316 1317 public: 1318 void trace(JSTracer* trc); 1319 1320 bool isEnclosingScript() const { 1321 return (data_ & TagMask) == EnclosingScriptTag; 1322 } 1323 bool isEnclosingScope() const { 1324 return (data_ & TagMask) == EnclosingScopeTag; 1325 } 1326 bool isWarmUpCount() const { return (data_ & TagMask) == WarmUpCountTag; } 1327 bool isJitScript() const { return (data_ & TagMask) == JitScriptTag; } 1328 1329 // NOTE: To change type safely, 'clear' the old tagged value and then 'init' 1330 // the new one. This will notify the GC appropriately. 1331 1332 BaseScript* toEnclosingScript() const { 1333 return getTaggedPtr<BaseScript*, EnclosingScriptTag>(); 1334 } 1335 inline void initEnclosingScript(BaseScript* enclosingScript); 1336 inline void clearEnclosingScript(); 1337 1338 Scope* toEnclosingScope() const { 1339 return getTaggedPtr<Scope*, EnclosingScopeTag>(); 1340 } 1341 inline void initEnclosingScope(Scope* enclosingScope); 1342 inline void clearEnclosingScope(); 1343 1344 uint32_t toWarmUpCount() const { 1345 MOZ_ASSERT(isWarmUpCount()); 1346 return data_ >> NumTagBits; 1347 } 1348 void resetWarmUpCount(uint32_t count) { 1349 MOZ_ASSERT(isWarmUpCount()); 1350 setWarmUpCount(count); 1351 } 1352 void incWarmUpCount() { 1353 MOZ_ASSERT(isWarmUpCount()); 1354 data_ += uintptr_t(1) << NumTagBits; 1355 } 1356 1357 jit::JitScript* toJitScript() const { 1358 return getTaggedPtr<jit::JitScript*, JitScriptTag>(); 1359 } 1360 void initJitScript(jit::JitScript* jitScript) { 1361 MOZ_ASSERT(isWarmUpCount()); 1362 setTaggedPtr<JitScriptTag>(jitScript); 1363 } 1364 void clearJitScript() { 1365 MOZ_ASSERT(isJitScript()); 1366 data_ = ResetState(); 1367 } 1368 } JS_HAZ_GC_POINTER; 1369 1370 static_assert(sizeof(ScriptWarmUpData) == sizeof(uintptr_t), 1371 "JIT code depends on ScriptWarmUpData being pointer-sized"); 1372 1373 // [SMDOC] - JSScript data layout (unshared) 1374 // 1375 // PrivateScriptData stores variable-length data associated with a script. 1376 // Abstractly a PrivateScriptData consists of the following: 1377 // 1378 // * A non-empty array of GCCellPtr in gcthings() 1379 // 1380 // Accessing this array just requires calling the appropriate public 1381 // Span-computing function. 1382 // 1383 // This class doesn't use the GC barrier wrapper classes. BaseScript::swapData 1384 // performs a manual pre-write barrier when detaching PrivateScriptData from a 1385 // script. 1386 class alignas(uintptr_t) PrivateScriptData final 1387 : public TrailingArray<PrivateScriptData> { 1388 private: 1389 uint32_t ngcthings = 0; 1390 1391 // Note: This is only defined for scripts with an enclosing scope. This 1392 // excludes lazy scripts with lazy parents. 1393 js::MemberInitializers memberInitializers_ = 1394 js::MemberInitializers::Invalid(); 1395 1396 // End of fields. 1397 1398 private: 1399 // Layout helpers 1400 Offset gcThingsOffset() { return offsetOfGCThings(); } 1401 Offset endOffset() const { 1402 uintptr_t size = ngcthings * sizeof(JS::GCCellPtr); 1403 return offsetOfGCThings() + size; 1404 } 1405 1406 // Initialize header and PackedSpans 1407 explicit PrivateScriptData(uint32_t ngcthings); 1408 1409 public: 1410 static constexpr size_t offsetOfGCThings() { 1411 return sizeof(PrivateScriptData); 1412 } 1413 1414 // Accessors for typed array spans. 1415 mozilla::Span<JS::GCCellPtr> gcthings() { 1416 Offset offset = offsetOfGCThings(); 1417 return mozilla::Span{offsetToPointer<JS::GCCellPtr>(offset), ngcthings}; 1418 } 1419 1420 void setMemberInitializers(MemberInitializers memberInitializers) { 1421 MOZ_ASSERT(memberInitializers_.valid == false, 1422 "Only init MemberInitializers once"); 1423 memberInitializers_ = memberInitializers; 1424 } 1425 const MemberInitializers& getMemberInitializers() { 1426 return memberInitializers_; 1427 } 1428 1429 // Allocate a new PrivateScriptData. Headers and GCCellPtrs are initialized. 1430 static PrivateScriptData* new_(JSContext* cx, uint32_t ngcthings); 1431 1432 static bool InitFromStencil( 1433 JSContext* cx, js::HandleScript script, 1434 const js::frontend::CompilationAtomCache& atomCache, 1435 const js::frontend::CompilationStencil& stencil, 1436 js::frontend::CompilationGCOutput& gcOutput, 1437 const js::frontend::ScriptIndex scriptIndex); 1438 1439 void trace(JSTracer* trc); 1440 1441 size_t allocationSize() const; 1442 1443 // PrivateScriptData has trailing data so isn't copyable or movable. 1444 PrivateScriptData(const PrivateScriptData&) = delete; 1445 PrivateScriptData& operator=(const PrivateScriptData&) = delete; 1446 }; 1447 1448 // An entry in the runtime's pendingCompressions_ list for a single 1449 // ScriptSource. 1450 // 1451 // It is not desirable to eagerly compress: if lazy functions that are tied to 1452 // the ScriptSource were to be executed relatively soon after parsing, they 1453 // would need to block on decompression, which hurts responsiveness. 1454 // 1455 // To this end, script sources are enqueued in a pending list by 1456 // ScriptSource::tryCompressOffThread. When a major GC occurs, we allocate and 1457 // submit SourceCompressionTasks for them. Currently, a script source is 1458 // considered ready 2 major GCs after being enqueued. 1459 class PendingSourceCompressionEntry { 1460 // The major GC number of the runtime when the entry was enqueued. 1461 uint64_t majorGCNumber_; 1462 1463 // The source to be compressed. 1464 RefPtr<ScriptSource> source_; 1465 1466 public: 1467 PendingSourceCompressionEntry(JSRuntime* rt, ScriptSource* source); 1468 1469 ScriptSource* source() const { return source_.get(); } 1470 uint64_t majorGCNumber() const { return majorGCNumber_; } 1471 bool shouldCancel() const { 1472 // If the refcount is exactly 1, then nothing else is holding on to the 1473 // ScriptSource, so no reason to compress it and we should cancel the 1474 // compression. 1475 return source_->refs == 1; 1476 } 1477 }; 1478 1479 // [SMDOC] Script Representation (js::BaseScript) 1480 // 1481 // A "script" corresponds to a JavaScript function or a top-level (global, eval, 1482 // module) body that will be executed using SpiderMonkey bytecode. Note that 1483 // special forms such as asm.js do not use bytecode or the BaseScript type. 1484 // 1485 // BaseScript may be generated directly from the parser/emitter, or by cloning 1486 // or deserializing another script. Cloning is typically used when a script is 1487 // needed in multiple realms and we would like to avoid re-compiling. 1488 // 1489 // A single script may be shared by multiple JSFunctions in a realm when those 1490 // function objects are used as closure. In this case, a single JSFunction is 1491 // considered canonical (and often does not escape to script directly). 1492 // 1493 // A BaseScript may be in "lazy" form where the parser performs a syntax-only 1494 // parse and saves minimal information. These lazy scripts must be recompiled 1495 // from the source (generating bytecode) before they can execute in a process 1496 // called "delazification". On GC memory pressure, a fully-compiled script may 1497 // be converted back into lazy form by "relazification". 1498 // 1499 // A fully-initialized BaseScript can be identified with `hasBytecode()` and 1500 // will have bytecode and set of GC-things such as scopes, inner-functions, and 1501 // object/string literals. This is referred to as a "non-lazy" script. 1502 // 1503 // A lazy script has either an enclosing script or scope. Each script needs to 1504 // know its enclosing scope in order to be fully compiled. If the parent is 1505 // still lazy we track that script and will need to compile it first to know our 1506 // own enclosing scope. This is because scope objects are not created until full 1507 // compilation and bytecode generation. 1508 // 1509 // 1510 // # Script Warm-Up # 1511 // 1512 // A script evolves its representation over time. As it becomes "hotter" we 1513 // attach a stack of additional data-structures generated by the JITs to 1514 // speed-up execution. This evolution may also be run in reverse, in order to 1515 // reduce memory usage. 1516 // 1517 // +-------------------------------------+ 1518 // | ScriptSource | 1519 // | Provides: Source | 1520 // | Engine: Parser | 1521 // +-------------------------------------+ 1522 // v 1523 // +-----------------------------------------------+ 1524 // | BaseScript | 1525 // | Provides: SourceExtent/Bindings | 1526 // | Engine: CompileLazyFunctionToStencil | 1527 // | /InstantiateStencilsForDelazify | 1528 // +-----------------------------------------------+ 1529 // v 1530 // +-------------------------------------+ 1531 // | ImmutableScriptData | 1532 // | Provides: Bytecode | 1533 // | Engine: Interpreter | 1534 // +-------------------------------------+ 1535 // v 1536 // +-------------------------------------+ 1537 // | JitScript | 1538 // | Provides: Inline Caches (ICs) | 1539 // | Engine: BaselineInterpreter | 1540 // +-------------------------------------+ 1541 // v 1542 // +-------------------------------------+ 1543 // | BaselineScript | 1544 // | Provides: Native Code | 1545 // | Engine: Baseline | 1546 // +-------------------------------------+ 1547 // v 1548 // +-------------------------------------+ 1549 // | IonScript | 1550 // | Provides: Optimized Native Code | 1551 // | Engine: IonMonkey | 1552 // +-------------------------------------+ 1553 // 1554 // NOTE: Scripts may be directly created with bytecode and skip the lazy script 1555 // form. This is always the case for top-level scripts. 1556 class BaseScript : public gc::TenuredCellWithNonGCPointer<uint8_t> { 1557 friend class js::gc::CellAllocator; 1558 1559 public: 1560 // Pointer to baseline->method()->raw(), ion->method()->raw(), a wasm jit 1561 // entry, the JIT's EnterInterpreter stub, or the lazy link stub. Must be 1562 // non-null (except on no-jit builds). This is stored in the cell header. 1563 uint8_t* jitCodeRaw() const { return headerPtr(); } 1564 1565 protected: 1566 // Multi-purpose value that changes type as the script warms up from lazy form 1567 // to interpreted-bytecode to JITs. See: ScriptWarmUpData type for more info. 1568 ScriptWarmUpData warmUpData_ = {}; 1569 1570 // For function scripts this is the canonical function, otherwise nullptr. 1571 const GCPtr<JSFunction*> function_ = {}; 1572 1573 // The ScriptSourceObject for this script. This is always same-compartment and 1574 // same-realm with this script. 1575 const GCPtr<ScriptSourceObject*> sourceObject_ = {}; 1576 1577 // Position of the function in the source buffer. Both in terms of line/column 1578 // and code-unit offset. 1579 const SourceExtent extent_ = {}; 1580 1581 // Immutable flags are a combination of parser options and bytecode 1582 // characteristics. These flags are preserved when serializing or copying this 1583 // script. 1584 const ImmutableScriptFlags immutableFlags_ = {}; 1585 1586 // Mutable flags store transient information used by subsystems such as the 1587 // debugger and the JITs. These flags are *not* preserved when serializing or 1588 // cloning since they are based on runtime state. 1589 MutableScriptFlags mutableFlags_ = {}; 1590 1591 // Variable-length data owned by this script. This stores one of: 1592 // - GC pointers that bytecode references. 1593 // - Inner-functions and bindings generated by syntax parse. 1594 // - Nullptr, if no bytecode or inner functions. 1595 // This is updated as script is delazified and relazified. 1596 GCStructPtr<PrivateScriptData*> data_; 1597 1598 // Shareable script data. This includes runtime-wide atom pointers, bytecode, 1599 // and various script note structures. If the script is currently lazy, this 1600 // will be nullptr. 1601 RefPtr<js::SharedImmutableScriptData> sharedData_ = {}; 1602 1603 // End of fields. 1604 1605 BaseScript(uint8_t* stubEntry, JSFunction* function, 1606 ScriptSourceObject* sourceObject, const SourceExtent& extent, 1607 uint32_t immutableFlags); 1608 1609 void setJitCodeRaw(uint8_t* code) { setHeaderPtr(code); } 1610 1611 public: 1612 static BaseScript* New(JSContext* cx, JS::Handle<JSFunction*> function, 1613 JS::Handle<js::ScriptSourceObject*> sourceObject, 1614 const js::SourceExtent& extent, 1615 uint32_t immutableFlags); 1616 1617 // Create a lazy BaseScript without initializing any gc-things. 1618 static BaseScript* CreateRawLazy(JSContext* cx, uint32_t ngcthings, 1619 HandleFunction fun, 1620 JS::Handle<ScriptSourceObject*> sourceObject, 1621 const SourceExtent& extent, 1622 uint32_t immutableFlags); 1623 1624 bool isUsingInterpreterTrampoline(JSRuntime* rt) const; 1625 1626 // Canonical function for the script, if it has a function. For top-level 1627 // scripts this is nullptr. 1628 JSFunction* function() const { return function_; } 1629 1630 JS::Realm* realm() const { return sourceObject()->realm(); } 1631 JS::Compartment* compartment() const { return sourceObject()->compartment(); } 1632 JS::Compartment* maybeCompartment() const { return compartment(); } 1633 inline JSPrincipals* principals() const; 1634 1635 ScriptSourceObject* sourceObject() const { return sourceObject_; } 1636 ScriptSource* scriptSource() const { return sourceObject()->source(); } 1637 ScriptSource* maybeForwardedScriptSource() const; 1638 1639 bool mutedErrors() const { return scriptSource()->mutedErrors(); } 1640 1641 const char* filename() const { return scriptSource()->filename(); } 1642 HashNumber filenameHash() const { return scriptSource()->filenameHash(); } 1643 const char* maybeForwardedFilename() const { 1644 return maybeForwardedScriptSource()->filename(); 1645 } 1646 1647 uint32_t sourceStart() const { return extent_.sourceStart; } 1648 uint32_t sourceEnd() const { return extent_.sourceEnd; } 1649 uint32_t sourceLength() const { 1650 return extent_.sourceEnd - extent_.sourceStart; 1651 } 1652 uint32_t toStringStart() const { return extent_.toStringStart; } 1653 uint32_t toStringEnd() const { return extent_.toStringEnd; } 1654 SourceExtent extent() const { return extent_; } 1655 1656 [[nodiscard]] bool appendSourceDataForToString(JSContext* cx, 1657 js::StringBuilder& buf); 1658 1659 // Line number (1-origin) 1660 uint32_t lineno() const { return extent_.lineno; } 1661 // Column number in UTF-16 code units 1662 JS::LimitedColumnNumberOneOrigin column() const { return extent_.column; } 1663 1664 JS::DelazificationOption delazificationMode() const { 1665 return scriptSource()->delazificationMode(); 1666 } 1667 1668 public: 1669 ImmutableScriptFlags immutableFlags() const { return immutableFlags_; } 1670 RO_IMMUTABLE_SCRIPT_FLAGS(immutableFlags_) 1671 RW_MUTABLE_SCRIPT_FLAGS(mutableFlags_) 1672 1673 bool hasEnclosingScript() const { return warmUpData_.isEnclosingScript(); } 1674 BaseScript* enclosingScript() const { 1675 return warmUpData_.toEnclosingScript(); 1676 } 1677 void setEnclosingScript(BaseScript* enclosingScript); 1678 1679 // Returns true is the script has an enclosing scope but no bytecode. It is 1680 // ready for delazification. 1681 // NOTE: The enclosing script must have been successfully compiled at some 1682 // point for the enclosing scope to exist. That script may have since been 1683 // GC'd, but we kept the scope live so we can still compile ourselves. 1684 bool isReadyForDelazification() const { 1685 return warmUpData_.isEnclosingScope(); 1686 } 1687 1688 Scope* enclosingScope() const; 1689 void setEnclosingScope(Scope* enclosingScope); 1690 Scope* releaseEnclosingScope(); 1691 1692 bool hasJitScript() const { return warmUpData_.isJitScript(); } 1693 jit::JitScript* jitScript() const { 1694 MOZ_ASSERT(hasJitScript()); 1695 return warmUpData_.toJitScript(); 1696 } 1697 jit::JitScript* maybeJitScript() const { 1698 return hasJitScript() ? jitScript() : nullptr; 1699 } 1700 1701 inline bool hasBaselineScript() const; 1702 inline bool hasIonScript() const; 1703 1704 bool hasPrivateScriptData() const { return data_ != nullptr; } 1705 1706 // Update data_ pointer while also informing GC MemoryUse tracking. 1707 void swapData(UniquePtr<PrivateScriptData>& other); 1708 1709 mozilla::Span<const JS::GCCellPtr> gcthings() const { 1710 return data_ ? data_->gcthings() : mozilla::Span<JS::GCCellPtr>(); 1711 } 1712 1713 // NOTE: This is only used to initialize a fresh script. 1714 mozilla::Span<JS::GCCellPtr> gcthingsForInit() { 1715 MOZ_ASSERT(!hasBytecode()); 1716 return data_ ? data_->gcthings() : mozilla::Span<JS::GCCellPtr>(); 1717 } 1718 1719 void setMemberInitializers(MemberInitializers memberInitializers) { 1720 MOZ_ASSERT(useMemberInitializers()); 1721 MOZ_ASSERT(data_); 1722 data_->setMemberInitializers(memberInitializers); 1723 } 1724 const MemberInitializers& getMemberInitializers() const { 1725 MOZ_ASSERT(data_); 1726 return data_->getMemberInitializers(); 1727 } 1728 1729 SharedImmutableScriptData* sharedData() const { return sharedData_; } 1730 void initSharedData(SharedImmutableScriptData* data) { 1731 MOZ_ASSERT(sharedData_ == nullptr); 1732 sharedData_ = data; 1733 } 1734 void freeSharedData() { sharedData_ = nullptr; } 1735 1736 // NOTE: Script only has bytecode if JSScript::fullyInitFromStencil completes 1737 // successfully. 1738 bool hasBytecode() const { 1739 if (sharedData_) { 1740 MOZ_ASSERT(data_); 1741 MOZ_ASSERT(warmUpData_.isWarmUpCount() || warmUpData_.isJitScript()); 1742 return true; 1743 } 1744 return false; 1745 } 1746 1747 public: 1748 static const JS::TraceKind TraceKind = JS::TraceKind::Script; 1749 1750 void traceChildren(JSTracer* trc); 1751 void finalize(JS::GCContext* gcx); 1752 1753 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { 1754 return mallocSizeOf(data_); 1755 } 1756 1757 inline JSScript* asJSScript(); 1758 1759 // JIT accessors 1760 static constexpr size_t offsetOfJitCodeRaw() { return offsetOfHeaderPtr(); } 1761 static constexpr size_t offsetOfPrivateData() { 1762 return offsetof(BaseScript, data_); 1763 } 1764 static constexpr size_t offsetOfSharedData() { 1765 return offsetof(BaseScript, sharedData_); 1766 } 1767 static size_t offsetOfImmutableFlags() { 1768 static_assert(sizeof(ImmutableScriptFlags) == sizeof(uint32_t)); 1769 return offsetof(BaseScript, immutableFlags_); 1770 } 1771 static constexpr size_t offsetOfMutableFlags() { 1772 static_assert(sizeof(MutableScriptFlags) == sizeof(uint32_t)); 1773 return offsetof(BaseScript, mutableFlags_); 1774 } 1775 static constexpr size_t offsetOfWarmUpData() { 1776 return offsetof(BaseScript, warmUpData_); 1777 } 1778 1779 #if defined(DEBUG) || defined(JS_JITSPEW) 1780 void dumpStringContent(js::GenericPrinter& out) const; 1781 #endif 1782 }; 1783 1784 extern void SweepScriptData(JSRuntime* rt); 1785 1786 } /* namespace js */ 1787 1788 class JSScript : public js::BaseScript { 1789 private: 1790 friend bool js::PrivateScriptData::InitFromStencil( 1791 JSContext* cx, js::HandleScript script, 1792 const js::frontend::CompilationAtomCache& atomCache, 1793 const js::frontend::CompilationStencil& stencil, 1794 js::frontend::CompilationGCOutput& gcOutput, 1795 const js::frontend::ScriptIndex scriptIndex); 1796 1797 private: 1798 using js::BaseScript::BaseScript; 1799 1800 public: 1801 static JSScript* Create(JSContext* cx, JS::Handle<JSFunction*> function, 1802 JS::Handle<js::ScriptSourceObject*> sourceObject, 1803 const js::SourceExtent& extent, 1804 js::ImmutableScriptFlags flags); 1805 1806 // NOTE: This should only be used while delazifying. 1807 static JSScript* CastFromLazy(js::BaseScript* lazy) { 1808 return static_cast<JSScript*>(lazy); 1809 } 1810 1811 // NOTE: If you use createPrivateScriptData directly instead of via 1812 // fullyInitFromStencil, you are responsible for notifying the debugger 1813 // after successfully creating the script. 1814 static bool createPrivateScriptData(JSContext* cx, 1815 JS::Handle<JSScript*> script, 1816 uint32_t ngcthings); 1817 1818 public: 1819 static bool fullyInitFromStencil( 1820 JSContext* cx, const js::frontend::CompilationAtomCache& atomCache, 1821 const js::frontend::CompilationStencil& stencil, 1822 js::frontend::CompilationGCOutput& gcOutput, js::HandleScript script, 1823 const js::frontend::ScriptIndex scriptIndex); 1824 1825 // Allocate a JSScript and initialize it with bytecode. This consumes 1826 // allocations within the stencil. 1827 static JSScript* fromStencil(JSContext* cx, 1828 js::frontend::CompilationAtomCache& atomCache, 1829 const js::frontend::CompilationStencil& stencil, 1830 js::frontend::CompilationGCOutput& gcOutput, 1831 js::frontend::ScriptIndex scriptIndex); 1832 1833 #ifdef DEBUG 1834 private: 1835 // Assert that jump targets are within the code array of the script. 1836 void assertValidJumpTargets() const; 1837 #endif 1838 1839 public: 1840 js::ImmutableScriptData* immutableScriptData() const { 1841 return sharedData_->get(); 1842 } 1843 1844 // Script bytecode is immutable after creation. 1845 jsbytecode* code() const { 1846 if (!sharedData_) { 1847 return nullptr; 1848 } 1849 return immutableScriptData()->code(); 1850 } 1851 1852 bool hasForceInterpreterOp() const { 1853 // JSOp::ForceInterpreter, if present, must be the first op. 1854 MOZ_ASSERT(length() >= 1); 1855 return JSOp(*code()) == JSOp::ForceInterpreter; 1856 } 1857 1858 js::AllBytecodesIterable allLocations() { 1859 return js::AllBytecodesIterable(this); 1860 } 1861 1862 js::BytecodeLocation location() { return js::BytecodeLocation(this, code()); } 1863 1864 size_t length() const { 1865 MOZ_ASSERT(sharedData_); 1866 return immutableScriptData()->codeLength(); 1867 } 1868 1869 jsbytecode* codeEnd() const { return code() + length(); } 1870 1871 jsbytecode* lastPC() const { 1872 jsbytecode* pc = codeEnd() - js::JSOpLength_RetRval; 1873 MOZ_ASSERT(JSOp(*pc) == JSOp::RetRval || JSOp(*pc) == JSOp::Return); 1874 return pc; 1875 } 1876 1877 // Note: ArgBytes is optional, but if specified then containsPC will also 1878 // check that the opcode arguments are in bounds. 1879 template <size_t ArgBytes = 0> 1880 bool containsPC(const jsbytecode* pc) const { 1881 MOZ_ASSERT_IF(ArgBytes, 1882 js::GetBytecodeLength(pc) == sizeof(jsbytecode) + ArgBytes); 1883 const jsbytecode* lastByte = pc + ArgBytes; 1884 return pc >= code() && lastByte < codeEnd(); 1885 } 1886 template <typename ArgType> 1887 bool containsPC(const jsbytecode* pc) const { 1888 return containsPC<sizeof(ArgType)>(pc); 1889 } 1890 1891 bool contains(const js::BytecodeLocation& loc) const { 1892 return containsPC(loc.toRawBytecode()); 1893 } 1894 1895 size_t pcToOffset(const jsbytecode* pc) const { 1896 MOZ_ASSERT(containsPC(pc)); 1897 return size_t(pc - code()); 1898 } 1899 1900 jsbytecode* offsetToPC(size_t offset) const { 1901 MOZ_ASSERT(offset < length()); 1902 return code() + offset; 1903 } 1904 1905 size_t mainOffset() const { return immutableScriptData()->mainOffset; } 1906 1907 // The fixed part of a stack frame is comprised of vars (in function and 1908 // module code) and block-scoped locals (in all kinds of code). 1909 size_t nfixed() const { return immutableScriptData()->nfixed; } 1910 1911 // Number of fixed slots reserved for slots that are always live. Only 1912 // nonzero for function or module code. 1913 size_t numAlwaysLiveFixedSlots() const; 1914 1915 // Calculate the number of fixed slots that are live at a particular bytecode. 1916 size_t calculateLiveFixed(jsbytecode* pc); 1917 1918 size_t nslots() const { return immutableScriptData()->nslots; } 1919 1920 unsigned numArgs() const; 1921 1922 inline js::Shape* initialEnvironmentShape() const; 1923 1924 bool functionHasParameterExprs() const; 1925 1926 bool functionAllowsParameterRedeclaration() const { 1927 // Parameter redeclaration is only allowed for non-strict functions with 1928 // simple parameter lists, which are neither arrow nor method functions. We 1929 // don't have a flag at hand to test the function kind, but we can still 1930 // test if the function is non-strict and has a simple parameter list by 1931 // checking |hasMappedArgsObj()|. (Mapped arguments objects are only 1932 // created for non-strict functions with simple parameter lists.) 1933 return hasMappedArgsObj(); 1934 } 1935 1936 size_t numICEntries() const { return immutableScriptData()->numICEntries; } 1937 1938 size_t funLength() const { return immutableScriptData()->funLength; } 1939 1940 void cacheForEval() { 1941 MOZ_ASSERT(isForEval()); 1942 // IsEvalCacheCandidate will make sure that there's nothing in this 1943 // script that would prevent reexecution even if isRunOnce is 1944 // true. So just pretend like we never ran this script. 1945 clearFlag(MutableFlags::HasRunOnce); 1946 } 1947 1948 /* 1949 * Arguments access (via JSOp::*Arg* opcodes) must access the canonical 1950 * location for the argument. If an arguments object exists AND it's mapped 1951 * ('arguments' aliases formals), then all access must go through the 1952 * arguments object. Otherwise, the local slot is the canonical location for 1953 * the arguments. Note: if a formal is aliased through the scope chain, then 1954 * script->formalIsAliased and JSOp::*Arg* opcodes won't be emitted at all. 1955 */ 1956 bool argsObjAliasesFormals() const { 1957 return needsArgsObj() && hasMappedArgsObj(); 1958 } 1959 1960 void updateJitCodeRaw(JSRuntime* rt); 1961 1962 bool isModule() const; 1963 js::ModuleObject* module() const; 1964 1965 bool isGlobalCode() const; 1966 1967 // Returns true if the script may read formal arguments on the stack 1968 // directly, via lazy arguments or a rest parameter. 1969 bool mayReadFrameArgsDirectly(); 1970 1971 static JSLinearString* sourceData(JSContext* cx, JS::HandleScript script); 1972 1973 #ifdef MOZ_VTUNE 1974 // Unique Method ID passed to the VTune profiler. Allows attribution of 1975 // different jitcode to the same source script. 1976 uint32_t vtuneMethodID(); 1977 #endif 1978 1979 public: 1980 /* Return whether this is a 'direct eval' script in a function scope. */ 1981 bool isDirectEvalInFunction() const; 1982 1983 /* 1984 * Return whether this script is a top-level script. 1985 * 1986 * If we evaluate some code which contains a syntax error, then we might 1987 * produce a JSScript which has no associated bytecode. Testing with 1988 * |code()| filters out this kind of scripts. 1989 * 1990 * If this script has a function associated to it, then it is not the 1991 * top-level of a file. 1992 */ 1993 bool isTopLevel() { return code() && !isFunction(); } 1994 1995 /* Ensure the script has a JitScript. */ 1996 inline bool ensureHasJitScript(JSContext* cx, js::jit::AutoKeepJitScripts&); 1997 1998 void maybeReleaseJitScript(JS::GCContext* gcx); 1999 void releaseJitScript(JS::GCContext* gcx); 2000 void releaseJitScriptOnFinalize(JS::GCContext* gcx); 2001 2002 inline js::jit::BaselineScript* baselineScript() const; 2003 inline js::jit::IonScript* ionScript() const; 2004 2005 inline bool isIonCompilingOffThread() const; 2006 inline bool canIonCompile() const; 2007 inline void disableIon(); 2008 2009 inline bool isBaselineCompilingOffThread() const; 2010 inline bool canBaselineCompile() const; 2011 inline void disableBaselineCompile(); 2012 2013 inline js::GlobalObject& global() const; 2014 inline bool hasGlobal(const js::GlobalObject* global) const; 2015 js::GlobalObject& uninlinedGlobal() const; 2016 2017 js::GCThingIndex bodyScopeIndex() const { 2018 return immutableScriptData()->bodyScopeIndex; 2019 } 2020 2021 js::Scope* bodyScope() const { return getScope(bodyScopeIndex()); } 2022 2023 js::Scope* outermostScope() const { 2024 // The body scope may not be the outermost scope in the script when 2025 // the decl env scope is present. 2026 return getScope(js::GCThingIndex::outermostScopeIndex()); 2027 } 2028 2029 bool functionHasExtraBodyVarScope() const { 2030 bool res = BaseScript::functionHasExtraBodyVarScope(); 2031 MOZ_ASSERT_IF(res, functionHasParameterExprs()); 2032 return res; 2033 } 2034 2035 js::VarScope* functionExtraBodyVarScope() const; 2036 2037 bool needsBodyEnvironment() const; 2038 2039 inline js::LexicalScope* maybeNamedLambdaScope() const; 2040 2041 // Drop script data and reset warmUpData to reference enclosing scope. 2042 void relazify(JSRuntime* rt); 2043 2044 private: 2045 bool createJitScript(JSContext* cx); 2046 2047 bool shareScriptData(JSContext* cx); 2048 2049 public: 2050 inline uint32_t getWarmUpCount() const; 2051 inline void incWarmUpCounter(); 2052 inline void resetWarmUpCounterForGC(); 2053 2054 inline void updateLastICStubCounter(); 2055 inline uint32_t warmUpCountAtLastICStub() const; 2056 2057 void resetWarmUpCounterToDelayIonCompilation(); 2058 2059 unsigned getWarmUpResetCount() const { 2060 constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK); 2061 return mutableFlags_ & MASK; 2062 } 2063 void incWarmUpResetCounter() { 2064 constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK); 2065 uint32_t newCount = getWarmUpResetCount() + 1; 2066 if (newCount <= MASK) { 2067 mutableFlags_ &= ~MASK; 2068 mutableFlags_ |= newCount; 2069 } 2070 } 2071 void resetWarmUpResetCounter() { 2072 constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK); 2073 mutableFlags_ &= ~MASK; 2074 } 2075 2076 public: 2077 bool initScriptCounts(JSContext* cx); 2078 js::ScriptCounts& getScriptCounts(); 2079 js::PCCounts* maybeGetPCCounts(jsbytecode* pc); 2080 const js::PCCounts* maybeGetThrowCounts(jsbytecode* pc); 2081 js::PCCounts* getThrowCounts(jsbytecode* pc); 2082 uint64_t getHitCount(jsbytecode* pc); 2083 void addIonCounts(js::jit::IonScriptCounts* ionCounts); 2084 js::jit::IonScriptCounts* getIonCounts(); 2085 void releaseScriptCounts(js::ScriptCounts* counts); 2086 void destroyScriptCounts(); 2087 void resetScriptCounts(); 2088 2089 jsbytecode* main() const { return code() + mainOffset(); } 2090 2091 js::BytecodeLocation mainLocation() const { 2092 return js::BytecodeLocation(this, main()); 2093 } 2094 2095 js::BytecodeLocation endLocation() const { 2096 return js::BytecodeLocation(this, codeEnd()); 2097 } 2098 2099 js::BytecodeLocation offsetToLocation(uint32_t offset) const { 2100 return js::BytecodeLocation(this, offsetToPC(offset)); 2101 } 2102 2103 void addSizeOfJitScript(mozilla::MallocSizeOf mallocSizeOf, 2104 size_t* sizeOfJitScript, 2105 size_t* sizeOfAllocSites) const; 2106 2107 mozilla::Span<const js::TryNote> trynotes() const { 2108 return immutableScriptData()->tryNotes(); 2109 } 2110 2111 mozilla::Span<const js::ScopeNote> scopeNotes() const { 2112 return immutableScriptData()->scopeNotes(); 2113 } 2114 2115 mozilla::Span<const uint32_t> resumeOffsets() const { 2116 return immutableScriptData()->resumeOffsets(); 2117 } 2118 2119 uint32_t tableSwitchCaseOffset(jsbytecode* pc, uint32_t caseIndex) const { 2120 MOZ_ASSERT(containsPC(pc)); 2121 MOZ_ASSERT(JSOp(*pc) == JSOp::TableSwitch); 2122 uint32_t firstResumeIndex = GET_RESUMEINDEX(pc + 3 * JUMP_OFFSET_LEN); 2123 return resumeOffsets()[firstResumeIndex + caseIndex]; 2124 } 2125 jsbytecode* tableSwitchCasePC(jsbytecode* pc, uint32_t caseIndex) const { 2126 return offsetToPC(tableSwitchCaseOffset(pc, caseIndex)); 2127 } 2128 2129 bool hasLoops(); 2130 2131 uint32_t numNotes() const { 2132 MOZ_ASSERT(sharedData_); 2133 return immutableScriptData()->noteLength(); 2134 } 2135 js::SrcNote* notes() const { 2136 MOZ_ASSERT(sharedData_); 2137 return immutableScriptData()->notes(); 2138 } 2139 js::SrcNote* notesEnd() const { 2140 MOZ_ASSERT(sharedData_); 2141 return immutableScriptData()->notes() + numNotes(); 2142 } 2143 2144 JSString* getString(js::GCThingIndex index) const { 2145 return &gcthings()[index].as<JSString>(); 2146 } 2147 2148 JSString* getString(jsbytecode* pc) const { 2149 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc)); 2150 MOZ_ASSERT(js::JOF_OPTYPE((JSOp)*pc) == JOF_STRING); 2151 return getString(GET_GCTHING_INDEX(pc)); 2152 } 2153 2154 bool atomizeString(JSContext* cx, jsbytecode* pc) { 2155 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc)); 2156 MOZ_ASSERT(js::JOF_OPTYPE((JSOp)*pc) == JOF_STRING); 2157 js::GCThingIndex index = GET_GCTHING_INDEX(pc); 2158 JSString* str = getString(index); 2159 if (str->isAtom()) { 2160 return true; 2161 } 2162 JSAtom* atom = js::AtomizeString(cx, str); 2163 if (!atom) { 2164 return false; 2165 } 2166 js::gc::CellPtrPreWriteBarrier(data_->gcthings()[index]); 2167 data_->gcthings()[index] = JS::GCCellPtr(atom); 2168 return true; 2169 } 2170 2171 JSAtom* getAtom(js::GCThingIndex index) const { 2172 return &gcthings()[index].as<JSString>().asAtom(); 2173 } 2174 2175 JSAtom* getAtom(jsbytecode* pc) const { 2176 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc)); 2177 MOZ_ASSERT(js::JOF_OPTYPE((JSOp)*pc) == JOF_ATOM); 2178 return getAtom(GET_GCTHING_INDEX(pc)); 2179 } 2180 2181 js::PropertyName* getName(js::GCThingIndex index) { 2182 return getAtom(index)->asPropertyName(); 2183 } 2184 2185 js::PropertyName* getName(jsbytecode* pc) const { 2186 return getAtom(pc)->asPropertyName(); 2187 } 2188 2189 JSObject* getObject(js::GCThingIndex index) const { 2190 MOZ_ASSERT(gcthings()[index].asCell()->isTenured()); 2191 return &gcthings()[index].as<JSObject>(); 2192 } 2193 2194 JSObject* getObject(const jsbytecode* pc) const { 2195 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc)); 2196 return getObject(GET_GCTHING_INDEX(pc)); 2197 } 2198 2199 js::SharedShape* getShape(js::GCThingIndex index) const { 2200 return &gcthings()[index].as<js::Shape>().asShared(); 2201 } 2202 2203 js::SharedShape* getShape(const jsbytecode* pc) const { 2204 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc)); 2205 return getShape(GET_GCTHING_INDEX(pc)); 2206 } 2207 2208 js::Scope* getScope(js::GCThingIndex index) const { 2209 return &gcthings()[index].as<js::Scope>(); 2210 } 2211 2212 js::Scope* getScope(jsbytecode* pc) const { 2213 // This method is used to get a scope directly using a JSOp with an 2214 // index. To search through ScopeNotes to look for a Scope using pc, 2215 // use lookupScope. 2216 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc)); 2217 MOZ_ASSERT(js::JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPE, 2218 "Did you mean to use lookupScope(pc)?"); 2219 return getScope(GET_GCTHING_INDEX(pc)); 2220 } 2221 2222 inline JSFunction* getFunction(js::GCThingIndex index) const; 2223 inline JSFunction* getFunction(jsbytecode* pc) const; 2224 2225 inline js::RegExpObject* getRegExp(js::GCThingIndex index) const; 2226 inline js::RegExpObject* getRegExp(jsbytecode* pc) const; 2227 2228 js::BigInt* getBigInt(js::GCThingIndex index) const { 2229 MOZ_ASSERT(gcthings()[index].asCell()->isTenured()); 2230 return &gcthings()[index].as<js::BigInt>(); 2231 } 2232 2233 js::BigInt* getBigInt(jsbytecode* pc) const { 2234 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc)); 2235 MOZ_ASSERT(js::JOF_OPTYPE(JSOp(*pc)) == JOF_BIGINT); 2236 return getBigInt(GET_GCTHING_INDEX(pc)); 2237 } 2238 2239 // The following 3 functions find the static scope just before the 2240 // execution of the instruction pointed to by pc. 2241 2242 js::Scope* lookupScope(const jsbytecode* pc) const; 2243 2244 js::Scope* innermostScope(const jsbytecode* pc) const; 2245 js::Scope* innermostScope() const { return innermostScope(main()); } 2246 2247 /* 2248 * The isEmpty method tells whether this script has code that computes any 2249 * result (not return value, result AKA normal completion value) other than 2250 * JSVAL_VOID, or any other effects. 2251 */ 2252 bool isEmpty() const { 2253 if (length() > 3) { 2254 return false; 2255 } 2256 2257 jsbytecode* pc = code(); 2258 if (noScriptRval() && JSOp(*pc) == JSOp::False) { 2259 ++pc; 2260 } 2261 return JSOp(*pc) == JSOp::RetRval; 2262 } 2263 2264 bool formalIsAliased(unsigned argSlot); 2265 bool anyFormalIsForwarded(); 2266 bool formalLivesInArgumentsObject(unsigned argSlot); 2267 2268 // See comment above 'debugMode' in Realm.h for explanation of 2269 // invariants of debuggee compartments, scripts, and frames. 2270 inline bool isDebuggee() const; 2271 2272 // A helper class to prevent relazification of the given function's script 2273 // while it's holding on to it. This class automatically roots the script. 2274 class AutoDelazify; 2275 friend class AutoDelazify; 2276 2277 class AutoDelazify { 2278 JS::RootedScript script_; 2279 JSContext* cx_; 2280 bool oldAllowRelazify_ = false; 2281 2282 public: 2283 explicit AutoDelazify(JSContext* cx, JS::HandleFunction fun = nullptr) 2284 : script_(cx), cx_(cx) { 2285 holdScript(fun); 2286 } 2287 2288 ~AutoDelazify() { dropScript(); } 2289 2290 void operator=(JS::HandleFunction fun) { 2291 dropScript(); 2292 holdScript(fun); 2293 } 2294 2295 operator JS::HandleScript() const { return script_; } 2296 explicit operator bool() const { return script_; } 2297 2298 private: 2299 void holdScript(JS::HandleFunction fun); 2300 void dropScript(); 2301 }; 2302 2303 #if defined(DEBUG) || defined(JS_JITSPEW) 2304 public: 2305 struct DumpOptions { 2306 bool recursive = false; 2307 bool runtimeData = false; 2308 }; 2309 2310 void dump(JSContext* cx); 2311 void dumpRecursive(JSContext* cx); 2312 2313 static bool dump(JSContext* cx, JS::Handle<JSScript*> script, 2314 DumpOptions& options, js::StringPrinter* sp); 2315 static bool dumpSrcNotes(JSContext* cx, JS::Handle<JSScript*> script, 2316 js::GenericPrinter* sp); 2317 static bool dumpTryNotes(JSContext* cx, JS::Handle<JSScript*> script, 2318 js::GenericPrinter* sp); 2319 static bool dumpScopeNotes(JSContext* cx, JS::Handle<JSScript*> script, 2320 js::GenericPrinter* sp); 2321 static bool dumpGCThings(JSContext* cx, JS::Handle<JSScript*> script, 2322 js::GenericPrinter* sp); 2323 #endif 2324 }; 2325 2326 namespace js { 2327 2328 struct ScriptAndCounts { 2329 /* This structure is stored and marked from the JSRuntime. */ 2330 JSScript* script; 2331 ScriptCounts scriptCounts; 2332 2333 inline explicit ScriptAndCounts(JSScript* script); 2334 inline ScriptAndCounts(ScriptAndCounts&& sac); 2335 2336 const PCCounts* maybeGetPCCounts(jsbytecode* pc) const { 2337 return scriptCounts.maybeGetPCCounts(script->pcToOffset(pc)); 2338 } 2339 const PCCounts* maybeGetThrowCounts(jsbytecode* pc) const { 2340 return scriptCounts.maybeGetThrowCounts(script->pcToOffset(pc)); 2341 } 2342 2343 jit::IonScriptCounts* getIonCounts() const { return scriptCounts.ionCounts_; } 2344 2345 void trace(JSTracer* trc) { 2346 TraceRoot(trc, &script, "ScriptAndCounts::script"); 2347 } 2348 }; 2349 2350 extern JS::UniqueChars FormatIntroducedFilename(const char* filename, 2351 uint32_t lineno, 2352 const char* introducer); 2353 2354 extern jsbytecode* LineNumberToPC(JSScript* script, unsigned lineno); 2355 2356 extern JS_PUBLIC_API unsigned GetScriptLineExtent( 2357 JSScript* script, JS::LimitedColumnNumberOneOrigin* columnp = nullptr); 2358 2359 #ifdef JS_CACHEIR_SPEW 2360 void maybeUpdateWarmUpCount(JSScript* script); 2361 void maybeSpewScriptFinalWarmUpCount(JSScript* script); 2362 #endif 2363 2364 } /* namespace js */ 2365 2366 namespace js { 2367 2368 extern unsigned PCToLineNumber( 2369 JSScript* script, jsbytecode* pc, 2370 JS::LimitedColumnNumberOneOrigin* columnp = nullptr); 2371 2372 extern unsigned PCToLineNumber( 2373 unsigned startLine, JS::LimitedColumnNumberOneOrigin startCol, 2374 SrcNote* notes, SrcNote* notesEnd, jsbytecode* code, jsbytecode* pc, 2375 JS::LimitedColumnNumberOneOrigin* columnp = nullptr); 2376 2377 /* 2378 * This function returns the file and line number of the script currently 2379 * executing on cx. If there is no current script executing on cx (e.g., a 2380 * native called directly through JSAPI (e.g., by setTimeout)), nullptr and 0 2381 * are returned as the file and line. 2382 */ 2383 extern void DescribeScriptedCallerForCompilation( 2384 JSContext* cx, MutableHandleScript maybeScript, const char** file, 2385 uint32_t* linenop, uint32_t* pcOffset, bool* mutedErrors); 2386 2387 /* 2388 * Like DescribeScriptedCallerForCompilation, but this function avoids looking 2389 * up the script/pc and the full linear scan to compute line number. 2390 */ 2391 extern void DescribeScriptedCallerForDirectEval( 2392 JSContext* cx, HandleScript script, jsbytecode* pc, const char** file, 2393 uint32_t* linenop, uint32_t* pcOffset, bool* mutedErrors); 2394 2395 bool CheckCompileOptionsMatch(const JS::ReadOnlyCompileOptions& options, 2396 js::ImmutableScriptFlags flags); 2397 2398 void FillImmutableFlagsFromCompileOptionsForTopLevel( 2399 const JS::ReadOnlyCompileOptions& options, js::ImmutableScriptFlags& flags); 2400 2401 void FillImmutableFlagsFromCompileOptionsForFunction( 2402 const JS::ReadOnlyCompileOptions& options, js::ImmutableScriptFlags& flags); 2403 2404 } /* namespace js */ 2405 2406 namespace JS { 2407 2408 template <> 2409 struct GCPolicy<js::ScriptLCovEntry> 2410 : public IgnoreGCPolicy<js::ScriptLCovEntry> {}; 2411 2412 #ifdef JS_CACHEIR_SPEW 2413 template <> 2414 struct GCPolicy<js::ScriptFinalWarmUpCountEntry> 2415 : public IgnoreGCPolicy<js::ScriptFinalWarmUpCountEntry> {}; 2416 #endif 2417 2418 namespace ubi { 2419 2420 template <> 2421 class Concrete<JSScript> : public Concrete<js::BaseScript> {}; 2422 2423 } // namespace ubi 2424 } // namespace JS 2425 2426 #endif /* vm_JSScript_h */