Bug 1753131 - Dispatch devicechange events even without an actively capturing MediaSt...
[gecko.git] / js / src / vm / JSScript.h
blobdcee049402b0ef1b899bf3544d8653b912b938ad
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* JS script descriptor. */
9 #ifndef vm_JSScript_h
10 #define vm_JSScript_h
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 #include "mozilla/Tuple.h"
19 #include "mozilla/UniquePtr.h"
20 #include "mozilla/Utf8.h"
21 #include "mozilla/Variant.h"
23 #include <type_traits> // std::is_same
24 #include <utility> // std::move
26 #include "jstypes.h"
28 #include "frontend/ScriptIndex.h" // ScriptIndex
29 #include "gc/Barrier.h"
30 #include "gc/Rooting.h"
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/StructuredSpewer.h"
37 #include "util/TrailingArray.h"
38 #include "vm/BigIntType.h"
39 #include "vm/BytecodeIterator.h"
40 #include "vm/BytecodeLocation.h"
41 #include "vm/BytecodeUtil.h"
42 #include "vm/JSAtom.h"
43 #include "vm/NativeObject.h"
44 #include "vm/ScopeKind.h" // ScopeKind
45 #include "vm/Shape.h"
46 #include "vm/SharedImmutableStringsCache.h"
47 #include "vm/SharedStencil.h" // js::GCThingIndex, js::SourceExtent, js::SharedImmutableScriptData, MemberInitializers
48 #include "vm/StencilEnums.h" // SourceRetrievable
49 #include "vm/Time.h"
51 namespace JS {
52 struct ScriptSourceInfo;
53 template <typename UnitT>
54 class SourceText;
55 } // namespace JS
57 namespace js {
59 class ScriptSource;
61 class VarScope;
62 class LexicalScope;
64 namespace coverage {
65 class LCovSource;
66 } // namespace coverage
68 namespace gc {
69 class AllocSite;
70 } // namespace gc
72 namespace jit {
73 class AutoKeepJitScripts;
74 class BaselineScript;
75 class IonScript;
76 struct IonScriptCounts;
77 class JitScript;
78 } // namespace jit
80 class ModuleObject;
81 class RegExpObject;
82 class SourceCompressionTask;
83 class Shape;
84 class SrcNote;
85 class DebugScript;
87 namespace frontend {
88 struct CompilationStencil;
89 struct ExtensibleCompilationStencil;
90 struct CompilationGCOutput;
91 struct CompilationStencilMerger;
92 class StencilXDR;
93 } // namespace frontend
95 class ScriptCounts {
96 public:
97 typedef mozilla::Vector<PCCounts, 0, SystemAllocPolicy> PCCountsVector;
99 inline ScriptCounts();
100 inline explicit ScriptCounts(PCCountsVector&& jumpTargets);
101 inline ScriptCounts(ScriptCounts&& src);
102 inline ~ScriptCounts();
104 inline ScriptCounts& operator=(ScriptCounts&& src);
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;
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);
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;
119 // Throw counts are stored at the location of each throwing
120 // instruction. This function looks for the previous throw count.
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;
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);
131 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
133 bool traceWeak(JSTracer* trc) { return true; }
135 private:
136 friend class ::JSScript;
137 friend struct ScriptAndCounts;
139 // This sorted array is used to map an offset to the number of times a
140 // branch got visited.
141 PCCountsVector pcCounts_;
143 // This sorted vector is used to map an offset to the number of times an
144 // instruction throw.
145 PCCountsVector throwCounts_;
147 // Information about any Ion compilations for the script.
148 jit::IonScriptCounts* ionCounts_;
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.
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.
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>;
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 = mozilla::Tuple<coverage::LCovSource*, const char*>;
180 using ScriptLCovMap =
181 GCRekeyableHashMap<HeapPtr<BaseScript*>, ScriptLCovEntry,
182 DefaultHasher<HeapPtr<BaseScript*>>, SystemAllocPolicy>;
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 =
191 mozilla::Tuple<uint32_t, SharedImmutableString>;
192 using ScriptFinalWarmUpCountMap =
193 GCRekeyableHashMap<HeapPtr<BaseScript*>, ScriptFinalWarmUpCountEntry,
194 DefaultHasher<HeapPtr<BaseScript*>>, SystemAllocPolicy>;
195 #endif
197 // As we execute JS sources that used lazy parsing, we may generate additional
198 // bytecode that we would like to include in caches if they are being used.
199 // There is a dependency cycle between JSScript / ScriptSource /
200 // CompilationStencil for this scenario so introduce this smart-ptr wrapper to
201 // avoid needing the full details of the stencil-merger in this file.
202 class StencilIncrementalEncoderPtr {
203 public:
204 frontend::CompilationStencilMerger* merger_ = nullptr;
206 StencilIncrementalEncoderPtr() = default;
207 ~StencilIncrementalEncoderPtr() { reset(); }
209 bool hasEncoder() const { return bool(merger_); }
211 void reset();
213 bool setInitial(JSContext* cx,
214 UniquePtr<frontend::ExtensibleCompilationStencil>&& initial);
216 bool addDelazification(JSContext* cx,
217 const frontend::CompilationStencil& delazification);
220 struct ScriptSourceChunk {
221 ScriptSource* ss = nullptr;
222 uint32_t chunk = 0;
224 ScriptSourceChunk() = default;
226 ScriptSourceChunk(ScriptSource* ss, uint32_t chunk) : ss(ss), chunk(chunk) {
227 MOZ_ASSERT(valid());
230 bool valid() const { return ss != nullptr; }
232 bool operator==(const ScriptSourceChunk& other) const {
233 return ss == other.ss && chunk == other.chunk;
237 struct ScriptSourceChunkHasher {
238 using Lookup = ScriptSourceChunk;
240 static HashNumber hash(const ScriptSourceChunk& ssc) {
241 return mozilla::AddToHash(DefaultHasher<ScriptSource*>::hash(ssc.ss),
242 ssc.chunk);
244 static bool match(const ScriptSourceChunk& c1, const ScriptSourceChunk& c2) {
245 return c1 == c2;
249 template <typename Unit>
250 using EntryUnits = mozilla::UniquePtr<Unit[], JS::FreePolicy>;
252 // The uncompressed source cache contains *either* UTF-8 source data *or*
253 // UTF-16 source data. ScriptSourceChunk implies a ScriptSource that
254 // contains either UTF-8 data or UTF-16 data, so the nature of the key to
255 // Map below indicates how each SourceData ought to be interpreted.
256 using SourceData = mozilla::UniquePtr<void, JS::FreePolicy>;
258 template <typename Unit>
259 inline SourceData ToSourceData(EntryUnits<Unit> chars) {
260 static_assert(std::is_same_v<SourceData::DeleterType,
261 typename EntryUnits<Unit>::DeleterType>,
262 "EntryUnits and SourceData must share the same deleter "
263 "type, that need not know the type of the data being freed, "
264 "for the upcast below to be safe");
265 return SourceData(chars.release());
268 class UncompressedSourceCache {
269 using Map = HashMap<ScriptSourceChunk, SourceData, ScriptSourceChunkHasher,
270 SystemAllocPolicy>;
272 public:
273 // Hold an entry in the source data cache and prevent it from being purged on
274 // GC.
275 class AutoHoldEntry {
276 UncompressedSourceCache* cache_ = nullptr;
277 ScriptSourceChunk sourceChunk_ = {};
278 SourceData data_ = nullptr;
280 public:
281 explicit AutoHoldEntry() = default;
283 ~AutoHoldEntry() {
284 if (cache_) {
285 MOZ_ASSERT(sourceChunk_.valid());
286 cache_->releaseEntry(*this);
290 template <typename Unit>
291 void holdUnits(EntryUnits<Unit> units) {
292 MOZ_ASSERT(!cache_);
293 MOZ_ASSERT(!sourceChunk_.valid());
294 MOZ_ASSERT(!data_);
296 data_ = ToSourceData(std::move(units));
299 private:
300 void holdEntry(UncompressedSourceCache* cache,
301 const ScriptSourceChunk& sourceChunk) {
302 // Initialise the holder for a specific cache and script source.
303 // This will hold on to the cached source chars in the event that
304 // the cache is purged.
305 MOZ_ASSERT(!cache_);
306 MOZ_ASSERT(!sourceChunk_.valid());
307 MOZ_ASSERT(!data_);
309 cache_ = cache;
310 sourceChunk_ = sourceChunk;
313 void deferDelete(SourceData data) {
314 // Take ownership of source chars now the cache is being purged. Remove
315 // our reference to the ScriptSource which might soon be destroyed.
316 MOZ_ASSERT(cache_);
317 MOZ_ASSERT(sourceChunk_.valid());
318 MOZ_ASSERT(!data_);
320 cache_ = nullptr;
321 sourceChunk_ = ScriptSourceChunk();
323 data_ = std::move(data);
326 const ScriptSourceChunk& sourceChunk() const { return sourceChunk_; }
327 friend class UncompressedSourceCache;
330 private:
331 UniquePtr<Map> map_ = nullptr;
332 AutoHoldEntry* holder_ = nullptr;
334 public:
335 UncompressedSourceCache() = default;
337 template <typename Unit>
338 const Unit* lookup(const ScriptSourceChunk& ssc, AutoHoldEntry& asp);
340 bool put(const ScriptSourceChunk& ssc, SourceData data, AutoHoldEntry& asp);
342 void purge();
344 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
346 private:
347 void holdEntry(AutoHoldEntry& holder, const ScriptSourceChunk& ssc);
348 void releaseEntry(AutoHoldEntry& holder);
351 template <typename Unit>
352 struct SourceTypeTraits;
354 template <>
355 struct SourceTypeTraits<mozilla::Utf8Unit> {
356 using CharT = char;
357 using SharedImmutableString = js::SharedImmutableString;
359 static const mozilla::Utf8Unit* units(const SharedImmutableString& string) {
360 // Casting |char| data to |Utf8Unit| is safe because |Utf8Unit|
361 // contains a |char|. See the long comment in |Utf8Unit|'s definition.
362 return reinterpret_cast<const mozilla::Utf8Unit*>(string.chars());
365 static char* toString(const mozilla::Utf8Unit* units) {
366 auto asUnsigned =
367 const_cast<unsigned char*>(mozilla::Utf8AsUnsignedChars(units));
368 return reinterpret_cast<char*>(asUnsigned);
371 static UniqueChars toCacheable(EntryUnits<mozilla::Utf8Unit> str) {
372 // The cache only stores strings of |char| or |char16_t|, and right now
373 // it seems best not to gunk up the cache with |Utf8Unit| too. So
374 // cache |Utf8Unit| strings by interpreting them as |char| strings.
375 char* chars = toString(str.release());
376 return UniqueChars(chars);
380 template <>
381 struct SourceTypeTraits<char16_t> {
382 using CharT = char16_t;
383 using SharedImmutableString = js::SharedImmutableTwoByteString;
385 static const char16_t* units(const SharedImmutableString& string) {
386 return string.chars();
389 static char16_t* toString(const char16_t* units) {
390 return const_cast<char16_t*>(units);
393 static UniqueTwoByteChars toCacheable(EntryUnits<char16_t> str) {
394 return UniqueTwoByteChars(std::move(str));
398 // Synchronously compress the source of |script|, for testing purposes.
399 [[nodiscard]] extern bool SynchronouslyCompressSource(
400 JSContext* cx, JS::Handle<BaseScript*> script);
402 // [SMDOC] ScriptSource
404 // This class abstracts over the source we used to compile from. The current
405 // representation may transition to different modes in order to save memory.
406 // Abstractly the source may be one of UTF-8 or UTF-16. The data itself may be
407 // unavailable, retrieveable-using-source-hook, compressed, or uncompressed. If
408 // source is retrieved or decompressed for use, we may update the ScriptSource
409 // to hold the result.
410 class ScriptSource {
411 // NOTE: While ScriptSources may be compressed off thread, they are only
412 // modified by the main thread, and all members are always safe to access
413 // on the main thread.
415 friend class SourceCompressionTask;
416 friend bool SynchronouslyCompressSource(JSContext* cx,
417 JS::Handle<BaseScript*> script);
419 friend class frontend::StencilXDR;
421 private:
422 // Common base class of the templated variants of PinnedUnits<T>.
423 class PinnedUnitsBase {
424 protected:
425 ScriptSource* source_;
427 explicit PinnedUnitsBase(ScriptSource* source) : source_(source) {}
430 public:
431 // Any users that wish to manipulate the char buffer of the ScriptSource
432 // needs to do so via PinnedUnits for GC safety. A GC may compress
433 // ScriptSources. If the source were initially uncompressed, then any raw
434 // pointers to the char buffer would now point to the freed, uncompressed
435 // chars. This is analogous to Rooted.
436 template <typename Unit>
437 class PinnedUnits : public PinnedUnitsBase {
438 const Unit* units_;
440 public:
441 PinnedUnits(JSContext* cx, ScriptSource* source,
442 UncompressedSourceCache::AutoHoldEntry& holder, size_t begin,
443 size_t len);
445 ~PinnedUnits();
447 const Unit* get() const { return units_; }
449 const typename SourceTypeTraits<Unit>::CharT* asChars() const {
450 return SourceTypeTraits<Unit>::toString(get());
454 private:
455 // Missing source text that isn't retrievable using the source hook. (All
456 // ScriptSources initially begin in this state. Users that are compiling
457 // source text will overwrite |data| to store a different state.)
458 struct Missing {};
460 // Source that can be retrieved using the registered source hook. |Unit|
461 // records the source type so that source-text coordinates in functions and
462 // scripts that depend on this |ScriptSource| are correct.
463 template <typename Unit>
464 struct Retrievable {
465 // The source hook and script URL required to retrieve source are stored
466 // elsewhere, so nothing is needed here. It'd be better hygiene to store
467 // something source-hook-like in each |ScriptSource| that needs it, but that
468 // requires reimagining a source-hook API that currently depends on source
469 // hooks being uniquely-owned pointers...
472 // Uncompressed source text. Templates distinguish if we are interconvertable
473 // to |Retrievable| or not.
474 template <typename Unit>
475 class UncompressedData {
476 typename SourceTypeTraits<Unit>::SharedImmutableString string_;
478 public:
479 explicit UncompressedData(
480 typename SourceTypeTraits<Unit>::SharedImmutableString str)
481 : string_(std::move(str)) {}
483 const Unit* units() const { return SourceTypeTraits<Unit>::units(string_); }
485 size_t length() const { return string_.length(); }
488 template <typename Unit, SourceRetrievable CanRetrieve>
489 class Uncompressed : public UncompressedData<Unit> {
490 using Base = UncompressedData<Unit>;
492 public:
493 using Base::Base;
496 // Compressed source text. Templates distinguish if we are interconvertable
497 // to |Retrievable| or not.
498 template <typename Unit>
499 struct CompressedData {
500 // Single-byte compressed text, regardless whether the original text
501 // was single-byte or two-byte.
502 SharedImmutableString raw;
503 size_t uncompressedLength;
505 CompressedData(SharedImmutableString raw, size_t uncompressedLength)
506 : raw(std::move(raw)), uncompressedLength(uncompressedLength) {}
509 template <typename Unit, SourceRetrievable CanRetrieve>
510 struct Compressed : public CompressedData<Unit> {
511 using Base = CompressedData<Unit>;
513 public:
514 using Base::Base;
517 // The set of currently allowed encoding modes.
518 using SourceType =
519 mozilla::Variant<Compressed<mozilla::Utf8Unit, SourceRetrievable::Yes>,
520 Uncompressed<mozilla::Utf8Unit, SourceRetrievable::Yes>,
521 Compressed<mozilla::Utf8Unit, SourceRetrievable::No>,
522 Uncompressed<mozilla::Utf8Unit, SourceRetrievable::No>,
523 Compressed<char16_t, SourceRetrievable::Yes>,
524 Uncompressed<char16_t, SourceRetrievable::Yes>,
525 Compressed<char16_t, SourceRetrievable::No>,
526 Uncompressed<char16_t, SourceRetrievable::No>,
527 Retrievable<mozilla::Utf8Unit>, Retrievable<char16_t>,
528 Missing>;
531 // Start of fields.
534 mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refs = {};
536 // An id for this source that is unique across the process. This can be used
537 // to refer to this source from places that don't want to hold a strong
538 // reference on the source itself.
540 // This is a 32 bit ID and could overflow, in which case the ID will not be
541 // unique anymore.
542 uint32_t id_ = 0;
544 // Source data (as a mozilla::Variant).
545 SourceType data = SourceType(Missing());
547 // If the GC calls triggerConvertToCompressedSource with PinnedUnits present,
548 // the last PinnedUnits instance will install the compressed chars upon
549 // destruction.
551 // Retrievability isn't part of the type here because uncompressed->compressed
552 // transitions must preserve existing retrievability.
553 struct ReaderInstances {
554 size_t count = 0;
555 mozilla::MaybeOneOf<CompressedData<mozilla::Utf8Unit>,
556 CompressedData<char16_t>>
557 pendingCompressed;
559 ExclusiveData<ReaderInstances> readers_;
561 // True if an associated SourceCompressionTask was ever created.
562 bool hadCompressionTask_ = false;
564 // The filename of this script.
565 SharedImmutableString filename_;
567 // If this ScriptSource was generated by a code-introduction mechanism such
568 // as |eval| or |new Function|, the debugger needs access to the "raw"
569 // filename of the top-level script that contains the eval-ing code. To
570 // keep track of this, we must preserve the original outermost filename (of
571 // the original introducer script), so that instead of a filename of
572 // "foo.js line 30 > eval line 10 > Function", we can obtain the original
573 // raw filename of "foo.js".
575 // In the case described above, this field will be set to to the original raw
576 // filename from above, otherwise it will be mozilla::Nothing.
577 SharedImmutableString introducerFilename_;
579 SharedImmutableTwoByteString displayURL_;
580 SharedImmutableTwoByteString sourceMapURL_;
582 // The bytecode cache encoder is used to encode only the content of function
583 // which are delazified. If this value is not nullptr, then each delazified
584 // function should be recorded before their first execution.
585 StencilIncrementalEncoderPtr xdrEncoder_;
587 // A string indicating how this source code was introduced into the system.
588 // This is a constant, statically allocated C string, so does not need memory
589 // management.
590 const char* introductionType_ = nullptr;
592 // Bytecode offset in caller script that generated this code. This is
593 // present for eval-ed code, as well as "new Function(...)"-introduced
594 // scripts.
595 mozilla::Maybe<uint32_t> introductionOffset_;
597 // If this source is for Function constructor, the position of ")" after
598 // parameter list in the source. This is used to get function body.
599 // 0 for other cases.
600 uint32_t parameterListEnd_ = 0;
602 // Line number within the file where this source starts.
603 uint32_t startLine_ = 0;
605 // See: CompileOptions::mutedErrors.
606 bool mutedErrors_ = false;
609 // End of fields.
612 // How many ids have been handed out to sources.
613 static mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent> idCount_;
615 template <typename Unit>
616 const Unit* chunkUnits(JSContext* cx,
617 UncompressedSourceCache::AutoHoldEntry& holder,
618 size_t chunk);
620 // Return a string containing the chars starting at |begin| and ending at
621 // |begin + len|.
623 // Warning: this is *not* GC-safe! Any chars to be handed out must use
624 // PinnedUnits. See comment below.
625 template <typename Unit>
626 const Unit* units(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp,
627 size_t begin, size_t len);
629 public:
630 // When creating a JSString* from TwoByte source characters, we don't try to
631 // to deflate to Latin1 for longer strings, because this can be slow.
632 static const size_t SourceDeflateLimit = 100;
634 explicit ScriptSource()
635 : id_(++idCount_), readers_(js::mutexid::SourceCompression) {}
636 ~ScriptSource() { MOZ_ASSERT(refs == 0); }
638 void AddRef() { refs++; }
639 void Release() {
640 MOZ_ASSERT(refs != 0);
641 if (--refs == 0) {
642 js_delete(this);
645 [[nodiscard]] bool initFromOptions(JSContext* cx,
646 const JS::ReadOnlyCompileOptions& options);
649 * The minimum script length (in code units) necessary for a script to be
650 * eligible to be compressed.
652 static constexpr size_t MinimumCompressibleLength = 256;
654 SharedImmutableString getOrCreateStringZ(JSContext* cx, UniqueChars&& str);
655 SharedImmutableTwoByteString getOrCreateStringZ(JSContext* cx,
656 UniqueTwoByteChars&& str);
658 private:
659 class LoadSourceMatcher;
661 public:
662 // Attempt to load usable source for |ss| -- source text on which substring
663 // operations and the like can be performed. On success return true and set
664 // |*loaded| to indicate whether usable source could be loaded; otherwise
665 // return false.
666 static bool loadSource(JSContext* cx, ScriptSource* ss, bool* loaded);
668 // Assign source data from |srcBuf| to this recently-created |ScriptSource|.
669 template <typename Unit>
670 [[nodiscard]] bool assignSource(JSContext* cx,
671 const JS::ReadOnlyCompileOptions& options,
672 JS::SourceText<Unit>& srcBuf);
674 bool hasSourceText() const {
675 return hasUncompressedSource() || hasCompressedSource();
678 private:
679 template <typename Unit>
680 struct UncompressedDataMatcher {
681 template <SourceRetrievable CanRetrieve>
682 const UncompressedData<Unit>* operator()(
683 const Uncompressed<Unit, CanRetrieve>& u) {
684 return &u;
687 template <typename T>
688 const UncompressedData<Unit>* operator()(const T&) {
689 MOZ_CRASH(
690 "attempting to access uncompressed data in a ScriptSource not "
691 "containing it");
692 return nullptr;
696 public:
697 template <typename Unit>
698 const UncompressedData<Unit>* uncompressedData() {
699 return data.match(UncompressedDataMatcher<Unit>());
702 private:
703 template <typename Unit>
704 struct CompressedDataMatcher {
705 template <SourceRetrievable CanRetrieve>
706 const CompressedData<Unit>* operator()(
707 const Compressed<Unit, CanRetrieve>& c) {
708 return &c;
711 template <typename T>
712 const CompressedData<Unit>* operator()(const T&) {
713 MOZ_CRASH(
714 "attempting to access compressed data in a ScriptSource not "
715 "containing it");
716 return nullptr;
720 public:
721 template <typename Unit>
722 const CompressedData<Unit>* compressedData() {
723 return data.match(CompressedDataMatcher<Unit>());
726 private:
727 struct HasUncompressedSource {
728 template <typename Unit, SourceRetrievable CanRetrieve>
729 bool operator()(const Uncompressed<Unit, CanRetrieve>&) {
730 return true;
733 template <typename Unit, SourceRetrievable CanRetrieve>
734 bool operator()(const Compressed<Unit, CanRetrieve>&) {
735 return false;
738 template <typename Unit>
739 bool operator()(const Retrievable<Unit>&) {
740 return false;
743 bool operator()(const Missing&) { return false; }
746 public:
747 bool hasUncompressedSource() const {
748 return data.match(HasUncompressedSource());
751 private:
752 template <typename Unit>
753 struct IsUncompressed {
754 template <SourceRetrievable CanRetrieve>
755 bool operator()(const Uncompressed<Unit, CanRetrieve>&) {
756 return true;
759 template <typename T>
760 bool operator()(const T&) {
761 return false;
765 public:
766 template <typename Unit>
767 bool isUncompressed() const {
768 return data.match(IsUncompressed<Unit>());
771 private:
772 struct HasCompressedSource {
773 template <typename Unit, SourceRetrievable CanRetrieve>
774 bool operator()(const Compressed<Unit, CanRetrieve>&) {
775 return true;
778 template <typename T>
779 bool operator()(const T&) {
780 return false;
784 public:
785 bool hasCompressedSource() const { return data.match(HasCompressedSource()); }
787 private:
788 template <typename Unit>
789 struct IsCompressed {
790 template <SourceRetrievable CanRetrieve>
791 bool operator()(const Compressed<Unit, CanRetrieve>&) {
792 return true;
795 template <typename T>
796 bool operator()(const T&) {
797 return false;
801 public:
802 template <typename Unit>
803 bool isCompressed() const {
804 return data.match(IsCompressed<Unit>());
807 private:
808 template <typename Unit>
809 struct SourceTypeMatcher {
810 template <template <typename C, SourceRetrievable R> class Data,
811 SourceRetrievable CanRetrieve>
812 bool operator()(const Data<Unit, CanRetrieve>&) {
813 return true;
816 template <template <typename C, SourceRetrievable R> class Data,
817 typename NotUnit, SourceRetrievable CanRetrieve>
818 bool operator()(const Data<NotUnit, CanRetrieve>&) {
819 return false;
822 bool operator()(const Retrievable<Unit>&) {
823 MOZ_CRASH("source type only applies where actual text is available");
824 return false;
827 template <typename NotUnit>
828 bool operator()(const Retrievable<NotUnit>&) {
829 return false;
832 bool operator()(const Missing&) {
833 MOZ_CRASH("doesn't make sense to ask source type when missing");
834 return false;
838 public:
839 template <typename Unit>
840 bool hasSourceType() const {
841 return data.match(SourceTypeMatcher<Unit>());
844 private:
845 struct UncompressedLengthMatcher {
846 template <typename Unit, SourceRetrievable CanRetrieve>
847 size_t operator()(const Uncompressed<Unit, CanRetrieve>& u) {
848 return u.length();
851 template <typename Unit, SourceRetrievable CanRetrieve>
852 size_t operator()(const Compressed<Unit, CanRetrieve>& u) {
853 return u.uncompressedLength;
856 template <typename Unit>
857 size_t operator()(const Retrievable<Unit>&) {
858 MOZ_CRASH("ScriptSource::length on a missing-but-retrievable source");
859 return 0;
862 size_t operator()(const Missing& m) {
863 MOZ_CRASH("ScriptSource::length on a missing source");
864 return 0;
868 public:
869 size_t length() const {
870 MOZ_ASSERT(hasSourceText());
871 return data.match(UncompressedLengthMatcher());
874 JSLinearString* substring(JSContext* cx, size_t start, size_t stop);
875 JSLinearString* substringDontDeflate(JSContext* cx, size_t start,
876 size_t stop);
878 [[nodiscard]] bool appendSubstring(JSContext* cx, js::StringBuffer& buf,
879 size_t start, size_t stop);
881 void setParameterListEnd(uint32_t parameterListEnd) {
882 parameterListEnd_ = parameterListEnd;
885 bool isFunctionBody() { return parameterListEnd_ != 0; }
886 JSLinearString* functionBodyString(JSContext* cx);
888 void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
889 JS::ScriptSourceInfo* info) const;
891 private:
892 // Overwrites |data| with the uncompressed data from |source|.
894 // This function asserts nothing about |data|. Users should use assertions to
895 // double-check their own understandings of the |data| state transition being
896 // performed.
897 template <typename Unit>
898 [[nodiscard]] bool setUncompressedSourceHelper(JSContext* cx,
899 EntryUnits<Unit>&& source,
900 size_t length,
901 SourceRetrievable retrievable);
903 public:
904 // Initialize a fresh |ScriptSource| with unretrievable, uncompressed source.
905 template <typename Unit>
906 [[nodiscard]] bool initializeUnretrievableUncompressedSource(
907 JSContext* cx, EntryUnits<Unit>&& source, size_t length);
909 // Set the retrieved source for a |ScriptSource| whose source was recorded as
910 // missing but retrievable.
911 template <typename Unit>
912 [[nodiscard]] bool setRetrievedSource(JSContext* cx,
913 EntryUnits<Unit>&& source,
914 size_t length);
916 [[nodiscard]] bool tryCompressOffThread(JSContext* cx);
918 // Called by the SourceCompressionTask constructor to indicate such a task was
919 // ever created.
920 void noteSourceCompressionTask() { hadCompressionTask_ = true; }
922 // *Trigger* the conversion of this ScriptSource from containing uncompressed
923 // |Unit|-encoded source to containing compressed source. Conversion may not
924 // be complete when this function returns: it'll be delayed if there's ongoing
925 // use of the uncompressed source via |PinnedUnits|, in which case conversion
926 // won't occur until the outermost |PinnedUnits| is destroyed.
928 // Compressed source is in bytes, no matter that |Unit| might be |char16_t|.
929 // |sourceLength| is the length in code units (not bytes) of the uncompressed
930 // source.
931 template <typename Unit>
932 void triggerConvertToCompressedSource(SharedImmutableString compressed,
933 size_t sourceLength);
935 // Initialize a fresh ScriptSource as containing unretrievable compressed
936 // source of the indicated original encoding.
937 template <typename Unit>
938 [[nodiscard]] bool initializeWithUnretrievableCompressedSource(
939 JSContext* cx, UniqueChars&& raw, size_t rawLength, size_t sourceLength);
941 private:
942 void performTaskWork(SourceCompressionTask* task);
944 struct TriggerConvertToCompressedSourceFromTask {
945 ScriptSource* const source_;
946 SharedImmutableString& compressed_;
948 TriggerConvertToCompressedSourceFromTask(ScriptSource* source,
949 SharedImmutableString& compressed)
950 : source_(source), compressed_(compressed) {}
952 template <typename Unit, SourceRetrievable CanRetrieve>
953 void operator()(const Uncompressed<Unit, CanRetrieve>&) {
954 source_->triggerConvertToCompressedSource<Unit>(std::move(compressed_),
955 source_->length());
958 template <typename Unit, SourceRetrievable CanRetrieve>
959 void operator()(const Compressed<Unit, CanRetrieve>&) {
960 MOZ_CRASH(
961 "can't set compressed source when source is already compressed -- "
962 "ScriptSource::tryCompressOffThread shouldn't have queued up this "
963 "task?");
966 template <typename Unit>
967 void operator()(const Retrievable<Unit>&) {
968 MOZ_CRASH("shouldn't compressing unloaded-but-retrievable source");
971 void operator()(const Missing&) {
972 MOZ_CRASH(
973 "doesn't make sense to set compressed source for missing source -- "
974 "ScriptSource::tryCompressOffThread shouldn't have queued up this "
975 "task?");
979 template <typename Unit>
980 void convertToCompressedSource(SharedImmutableString compressed,
981 size_t uncompressedLength);
983 template <typename Unit>
984 void performDelayedConvertToCompressedSource(
985 ExclusiveData<ReaderInstances>::Guard& g);
987 void triggerConvertToCompressedSourceFromTask(
988 SharedImmutableString compressed);
990 public:
991 const char* filename() const {
992 return filename_ ? filename_.chars() : nullptr;
994 [[nodiscard]] bool setFilename(JSContext* cx, const char* filename);
995 [[nodiscard]] bool setFilename(JSContext* cx, UniqueChars&& filename);
997 const char* introducerFilename() const {
998 return introducerFilename_ ? introducerFilename_.chars() : filename();
1000 [[nodiscard]] bool setIntroducerFilename(JSContext* cx, const char* filename);
1001 [[nodiscard]] bool setIntroducerFilename(JSContext* cx,
1002 UniqueChars&& filename);
1004 bool hasIntroductionType() const { return introductionType_; }
1005 const char* introductionType() const {
1006 MOZ_ASSERT(hasIntroductionType());
1007 return introductionType_;
1010 uint32_t id() const { return id_; }
1012 // Display URLs
1013 [[nodiscard]] bool setDisplayURL(JSContext* cx, const char16_t* url);
1014 [[nodiscard]] bool setDisplayURL(JSContext* cx, UniqueTwoByteChars&& url);
1015 bool hasDisplayURL() const { return bool(displayURL_); }
1016 const char16_t* displayURL() { return displayURL_.chars(); }
1018 // Source maps
1019 [[nodiscard]] bool setSourceMapURL(JSContext* cx, const char16_t* url);
1020 [[nodiscard]] bool setSourceMapURL(JSContext* cx, UniqueTwoByteChars&& url);
1021 bool hasSourceMapURL() const { return bool(sourceMapURL_); }
1022 const char16_t* sourceMapURL() { return sourceMapURL_.chars(); }
1024 bool mutedErrors() const { return mutedErrors_; }
1026 uint32_t startLine() const { return startLine_; }
1028 bool hasIntroductionOffset() const { return introductionOffset_.isSome(); }
1029 uint32_t introductionOffset() const { return introductionOffset_.value(); }
1030 void setIntroductionOffset(uint32_t offset) {
1031 MOZ_ASSERT(!hasIntroductionOffset());
1032 MOZ_ASSERT(offset <= (uint32_t)INT32_MAX);
1033 introductionOffset_.emplace(offset);
1036 // Return wether an XDR encoder is present or not.
1037 bool hasEncoder() const { return xdrEncoder_.hasEncoder(); }
1039 [[nodiscard]] bool startIncrementalEncoding(
1040 JSContext* cx,
1041 UniquePtr<frontend::ExtensibleCompilationStencil>&& initial);
1043 [[nodiscard]] bool addDelazificationToIncrementalEncoding(
1044 JSContext* cx, const frontend::CompilationStencil& stencil);
1046 // Linearize the encoded content in the |buffer| provided as argument to
1047 // |xdrEncodeTopLevel|, and free the XDR encoder. In case of errors, the
1048 // |buffer| is considered undefined.
1049 bool xdrFinalizeEncoder(JSContext* cx, JS::TranscodeBuffer& buffer);
1052 // [SMDOC] ScriptSourceObject
1054 // ScriptSourceObject stores the ScriptSource and GC pointers related to it.
1055 class ScriptSourceObject : public NativeObject {
1056 static const JSClassOps classOps_;
1058 public:
1059 static const JSClass class_;
1061 static void finalize(JSFreeOp* fop, JSObject* obj);
1063 static ScriptSourceObject* create(JSContext* cx, ScriptSource* source);
1065 // Initialize those properties of this ScriptSourceObject whose values
1066 // are provided by |options|, re-wrapping as necessary.
1067 static bool initFromOptions(JSContext* cx, HandleScriptSourceObject source,
1068 const JS::InstantiateOptions& options);
1070 static bool initElementProperties(JSContext* cx,
1071 HandleScriptSourceObject source,
1072 HandleString elementAttrName);
1074 bool hasSource() const { return !getReservedSlot(SOURCE_SLOT).isUndefined(); }
1075 ScriptSource* source() const {
1076 return static_cast<ScriptSource*>(getReservedSlot(SOURCE_SLOT).toPrivate());
1079 JSObject* unwrappedElement(JSContext* cx) const;
1081 const Value& unwrappedElementAttributeName() const {
1082 MOZ_ASSERT(isInitialized());
1083 const Value& v = getReservedSlot(ELEMENT_PROPERTY_SLOT);
1084 MOZ_ASSERT(!v.isMagic());
1085 return v;
1087 BaseScript* unwrappedIntroductionScript() const {
1088 MOZ_ASSERT(isInitialized());
1089 Value value = getReservedSlot(INTRODUCTION_SCRIPT_SLOT);
1090 if (value.isUndefined()) {
1091 return nullptr;
1093 return value.toGCThing()->as<BaseScript>();
1096 void setPrivate(JSRuntime* rt, const Value& value);
1098 void setIntroductionScript(const Value& introductionScript) {
1099 setReservedSlot(INTRODUCTION_SCRIPT_SLOT, introductionScript);
1102 Value getPrivate() const {
1103 MOZ_ASSERT(isInitialized());
1104 Value value = getReservedSlot(PRIVATE_SLOT);
1105 return value;
1108 private:
1109 #ifdef DEBUG
1110 bool isInitialized() const {
1111 Value element = getReservedSlot(ELEMENT_PROPERTY_SLOT);
1112 if (element.isMagic(JS_GENERIC_MAGIC)) {
1113 return false;
1115 return !getReservedSlot(INTRODUCTION_SCRIPT_SLOT).isMagic(JS_GENERIC_MAGIC);
1117 #endif
1119 enum {
1120 SOURCE_SLOT = 0,
1121 ELEMENT_PROPERTY_SLOT,
1122 INTRODUCTION_SCRIPT_SLOT,
1123 PRIVATE_SLOT,
1124 RESERVED_SLOTS
1128 // ScriptWarmUpData represents a pointer-sized field in BaseScript that stores
1129 // one of the following using low-bit tags:
1131 // * The enclosing BaseScript. This is only used while this script is lazy and
1132 // its containing script is also lazy. This outer script must be compiled
1133 // before the current script can in order to correctly build the scope chain.
1135 // * The enclosing Scope. This is only used while this script is lazy and its
1136 // containing script is compiled. This is the outer scope chain that will be
1137 // used to compile this scipt.
1139 // * The script's warm-up count. This is only used until the script has a
1140 // JitScript. The Baseline Interpreter and JITs use the warm-up count stored
1141 // in JitScript.
1143 // * A pointer to the JitScript, when the script is warm enough for the Baseline
1144 // Interpreter.
1146 class ScriptWarmUpData {
1147 uintptr_t data_ = ResetState();
1149 private:
1150 static constexpr uintptr_t NumTagBits = 2;
1151 static constexpr uint32_t MaxWarmUpCount = UINT32_MAX >> NumTagBits;
1153 public:
1154 // Public only for the JITs.
1155 static constexpr uintptr_t TagMask = (1 << NumTagBits) - 1;
1156 static constexpr uintptr_t JitScriptTag = 0;
1157 static constexpr uintptr_t EnclosingScriptTag = 1;
1158 static constexpr uintptr_t EnclosingScopeTag = 2;
1159 static constexpr uintptr_t WarmUpCountTag = 3;
1161 private:
1162 // A gc-safe value to clear to.
1163 constexpr uintptr_t ResetState() { return 0 | WarmUpCountTag; }
1165 template <uintptr_t Tag>
1166 inline void setTaggedPtr(void* ptr) {
1167 static_assert(Tag <= TagMask, "Tag must fit in TagMask");
1168 MOZ_ASSERT((uintptr_t(ptr) & TagMask) == 0);
1169 data_ = uintptr_t(ptr) | Tag;
1172 template <typename T, uintptr_t Tag>
1173 inline T getTaggedPtr() const {
1174 static_assert(Tag <= TagMask, "Tag must fit in TagMask");
1175 MOZ_ASSERT((data_ & TagMask) == Tag);
1176 return reinterpret_cast<T>(data_ & ~TagMask);
1179 void setWarmUpCount(uint32_t count) {
1180 if (count > MaxWarmUpCount) {
1181 count = MaxWarmUpCount;
1183 data_ = (uintptr_t(count) << NumTagBits) | WarmUpCountTag;
1186 public:
1187 void trace(JSTracer* trc);
1189 bool isEnclosingScript() const {
1190 return (data_ & TagMask) == EnclosingScriptTag;
1192 bool isEnclosingScope() const {
1193 return (data_ & TagMask) == EnclosingScopeTag;
1195 bool isWarmUpCount() const { return (data_ & TagMask) == WarmUpCountTag; }
1196 bool isJitScript() const { return (data_ & TagMask) == JitScriptTag; }
1198 // NOTE: To change type safely, 'clear' the old tagged value and then 'init'
1199 // the new one. This will notify the GC appropriately.
1201 BaseScript* toEnclosingScript() const {
1202 return getTaggedPtr<BaseScript*, EnclosingScriptTag>();
1204 inline void initEnclosingScript(BaseScript* enclosingScript);
1205 inline void clearEnclosingScript();
1207 Scope* toEnclosingScope() const {
1208 return getTaggedPtr<Scope*, EnclosingScopeTag>();
1210 inline void initEnclosingScope(Scope* enclosingScope);
1211 inline void clearEnclosingScope();
1213 uint32_t toWarmUpCount() const {
1214 MOZ_ASSERT(isWarmUpCount());
1215 return data_ >> NumTagBits;
1217 void resetWarmUpCount(uint32_t count) {
1218 MOZ_ASSERT(isWarmUpCount());
1219 setWarmUpCount(count);
1221 void incWarmUpCount(uint32_t amount) {
1222 MOZ_ASSERT(isWarmUpCount());
1223 data_ += uintptr_t(amount) << NumTagBits;
1226 jit::JitScript* toJitScript() const {
1227 return getTaggedPtr<jit::JitScript*, JitScriptTag>();
1229 void initJitScript(jit::JitScript* jitScript) {
1230 MOZ_ASSERT(isWarmUpCount());
1231 setTaggedPtr<JitScriptTag>(jitScript);
1233 void clearJitScript() {
1234 MOZ_ASSERT(isJitScript());
1235 data_ = ResetState();
1237 } JS_HAZ_GC_POINTER;
1239 static_assert(sizeof(ScriptWarmUpData) == sizeof(uintptr_t),
1240 "JIT code depends on ScriptWarmUpData being pointer-sized");
1242 // [SMDOC] - JSScript data layout (unshared)
1244 // PrivateScriptData stores variable-length data associated with a script.
1245 // Abstractly a PrivateScriptData consists of the following:
1247 // * A non-empty array of GCCellPtr in gcthings()
1249 // Accessing this array just requires calling the appropriate public
1250 // Span-computing function.
1252 // This class doesn't use the GC barrier wrapper classes. BaseScript::swapData
1253 // performs a manual pre-write barrier when detaching PrivateScriptData from a
1254 // script.
1255 class alignas(uintptr_t) PrivateScriptData final : public TrailingArray {
1256 private:
1257 uint32_t ngcthings = 0;
1259 // Note: This is only defined for scripts with an enclosing scope. This
1260 // excludes lazy scripts with lazy parents.
1261 js::MemberInitializers memberInitializers_ =
1262 js::MemberInitializers::Invalid();
1264 // End of fields.
1266 private:
1267 // Layout helpers
1268 Offset gcThingsOffset() { return offsetOfGCThings(); }
1269 Offset endOffset() const {
1270 uintptr_t size = ngcthings * sizeof(JS::GCCellPtr);
1271 return offsetOfGCThings() + size;
1274 // Initialize header and PackedSpans
1275 explicit PrivateScriptData(uint32_t ngcthings);
1277 public:
1278 static constexpr size_t offsetOfGCThings() {
1279 return sizeof(PrivateScriptData);
1282 // Accessors for typed array spans.
1283 mozilla::Span<JS::GCCellPtr> gcthings() {
1284 Offset offset = offsetOfGCThings();
1285 return mozilla::Span{offsetToPointer<JS::GCCellPtr>(offset), ngcthings};
1288 void setMemberInitializers(MemberInitializers memberInitializers) {
1289 MOZ_ASSERT(memberInitializers_.valid == false,
1290 "Only init MemberInitializers once");
1291 memberInitializers_ = memberInitializers;
1293 const MemberInitializers& getMemberInitializers() {
1294 return memberInitializers_;
1297 // Allocate a new PrivateScriptData. Headers and GCCellPtrs are initialized.
1298 static PrivateScriptData* new_(JSContext* cx, uint32_t ngcthings);
1300 static bool InitFromStencil(
1301 JSContext* cx, js::HandleScript script,
1302 const js::frontend::CompilationAtomCache& atomCache,
1303 const js::frontend::CompilationStencil& stencil,
1304 js::frontend::CompilationGCOutput& gcOutput,
1305 const js::frontend::ScriptIndex scriptIndex);
1307 void trace(JSTracer* trc);
1309 size_t allocationSize() const;
1311 // PrivateScriptData has trailing data so isn't copyable or movable.
1312 PrivateScriptData(const PrivateScriptData&) = delete;
1313 PrivateScriptData& operator=(const PrivateScriptData&) = delete;
1316 // [SMDOC] Script Representation (js::BaseScript)
1318 // A "script" corresponds to a JavaScript function or a top-level (global, eval,
1319 // module) body that will be executed using SpiderMonkey bytecode. Note that
1320 // special forms such as asm.js do not use bytecode or the BaseScript type.
1322 // BaseScript may be generated directly from the parser/emitter, or by cloning
1323 // or deserializing another script. Cloning is typically used when a script is
1324 // needed in multiple realms and we would like to avoid re-compiling.
1326 // A single script may be shared by multiple JSFunctions in a realm when those
1327 // function objects are used as closure. In this case, a single JSFunction is
1328 // considered canonical (and often does not escape to script directly).
1330 // A BaseScript may be in "lazy" form where the parser performs a syntax-only
1331 // parse and saves minimal information. These lazy scripts must be recompiled
1332 // from the source (generating bytecode) before they can execute in a process
1333 // called "delazification". On GC memory pressure, a fully-compiled script may
1334 // be converted back into lazy form by "relazification".
1336 // A fully-initialized BaseScript can be identified with `hasBytecode()` and
1337 // will have bytecode and set of GC-things such as scopes, inner-functions, and
1338 // object/string literals. This is referred to as a "non-lazy" script.
1340 // A lazy script has either an enclosing script or scope. Each script needs to
1341 // know its enclosing scope in order to be fully compiled. If the parent is
1342 // still lazy we track that script and will need to compile it first to know our
1343 // own enclosing scope. This is because scope objects are not created until full
1344 // compilation and bytecode generation.
1347 // # Script Warm-Up #
1349 // A script evolves its representation over time. As it becomes "hotter" we
1350 // attach a stack of additional data-structures generated by the JITs to
1351 // speed-up execution. This evolution may also be run in reverse, in order to
1352 // reduce memory usage.
1354 // +-------------------------------------+
1355 // | ScriptSource |
1356 // | Provides: Source |
1357 // | Engine: Parser |
1358 // +-------------------------------------+
1359 // v
1360 // +-----------------------------------------------+
1361 // | BaseScript |
1362 // | Provides: SourceExtent/Bindings |
1363 // | Engine: CompileLazyFunctionToStencil |
1364 // | /InstantiateStencilsForDelazify |
1365 // +-----------------------------------------------+
1366 // v
1367 // +-------------------------------------+
1368 // | ImmutableScriptData |
1369 // | Provides: Bytecode |
1370 // | Engine: Interpreter |
1371 // +-------------------------------------+
1372 // v
1373 // +-------------------------------------+
1374 // | JitScript |
1375 // | Provides: Inline Caches (ICs) |
1376 // | Engine: BaselineInterpreter |
1377 // +-------------------------------------+
1378 // v
1379 // +-------------------------------------+
1380 // | BaselineScript |
1381 // | Provides: Native Code |
1382 // | Engine: Baseline |
1383 // +-------------------------------------+
1384 // v
1385 // +-------------------------------------+
1386 // | IonScript |
1387 // | Provides: Optimized Native Code |
1388 // | Engine: IonMonkey |
1389 // +-------------------------------------+
1391 // NOTE: Scripts may be directly created with bytecode and skip the lazy script
1392 // form. This is always the case for top-level scripts.
1393 class BaseScript : public gc::TenuredCellWithNonGCPointer<uint8_t> {
1394 public:
1395 // Pointer to baseline->method()->raw(), ion->method()->raw(), a wasm jit
1396 // entry, the JIT's EnterInterpreter stub, or the lazy link stub. Must be
1397 // non-null (except on no-jit builds). This is stored in the cell header.
1398 uint8_t* jitCodeRaw() const { return headerPtr(); }
1400 protected:
1401 // Multi-purpose value that changes type as the script warms up from lazy form
1402 // to interpreted-bytecode to JITs. See: ScriptWarmUpData type for more info.
1403 ScriptWarmUpData warmUpData_ = {};
1405 // For function scripts this is the canonical function, otherwise nullptr.
1406 const GCPtr<JSFunction*> function_ = {};
1408 // The ScriptSourceObject for this script. This is always same-compartment and
1409 // same-realm with this script.
1410 const GCPtr<ScriptSourceObject*> sourceObject_ = {};
1412 // Position of the function in the source buffer. Both in terms of line/column
1413 // and code-unit offset.
1414 const SourceExtent extent_ = {};
1416 // Immutable flags are a combination of parser options and bytecode
1417 // characteristics. These flags are preserved when serializing or copying this
1418 // script.
1419 const ImmutableScriptFlags immutableFlags_ = {};
1421 // Mutable flags store transient information used by subsystems such as the
1422 // debugger and the JITs. These flags are *not* preserved when serializing or
1423 // cloning since they are based on runtime state.
1424 MutableScriptFlags mutableFlags_ = {};
1426 // Variable-length data owned by this script. This stores one of:
1427 // - GC pointers that bytecode references.
1428 // - Inner-functions and bindings generated by syntax parse.
1429 // - Nullptr, if no bytecode or inner functions.
1430 // This is updated as script is delazified and relazified.
1431 GCStructPtr<PrivateScriptData*> data_;
1433 // Shareable script data. This includes runtime-wide atom pointers, bytecode,
1434 // and various script note structures. If the script is currently lazy, this
1435 // will be nullptr.
1436 RefPtr<js::SharedImmutableScriptData> sharedData_ = {};
1438 // End of fields.
1440 BaseScript(uint8_t* stubEntry, JSFunction* function,
1441 ScriptSourceObject* sourceObject, const SourceExtent& extent,
1442 uint32_t immutableFlags);
1444 void setJitCodeRaw(uint8_t* code) { setHeaderPtr(code); }
1446 public:
1447 static BaseScript* New(JSContext* cx, JS::Handle<JSFunction*> function,
1448 js::HandleScriptSourceObject sourceObject,
1449 const js::SourceExtent& extent,
1450 uint32_t immutableFlags);
1452 // Create a lazy BaseScript without initializing any gc-things.
1453 static BaseScript* CreateRawLazy(JSContext* cx, uint32_t ngcthings,
1454 HandleFunction fun,
1455 HandleScriptSourceObject sourceObject,
1456 const SourceExtent& extent,
1457 uint32_t immutableFlags);
1459 bool isUsingInterpreterTrampoline(JSRuntime* rt) const;
1461 // Canonical function for the script, if it has a function. For top-level
1462 // scripts this is nullptr.
1463 JSFunction* function() const { return function_; }
1465 JS::Realm* realm() const { return sourceObject()->realm(); }
1466 JS::Compartment* compartment() const { return sourceObject()->compartment(); }
1467 JS::Compartment* maybeCompartment() const { return compartment(); }
1468 inline JSPrincipals* principals() const;
1470 ScriptSourceObject* sourceObject() const { return sourceObject_; }
1471 ScriptSource* scriptSource() const { return sourceObject()->source(); }
1472 ScriptSource* maybeForwardedScriptSource() const;
1474 bool mutedErrors() const { return scriptSource()->mutedErrors(); }
1476 const char* filename() const { return scriptSource()->filename(); }
1477 const char* maybeForwardedFilename() const {
1478 return maybeForwardedScriptSource()->filename();
1481 uint32_t sourceStart() const { return extent_.sourceStart; }
1482 uint32_t sourceEnd() const { return extent_.sourceEnd; }
1483 uint32_t sourceLength() const {
1484 return extent_.sourceEnd - extent_.sourceStart;
1486 uint32_t toStringStart() const { return extent_.toStringStart; }
1487 uint32_t toStringEnd() const { return extent_.toStringEnd; }
1488 SourceExtent extent() const { return extent_; }
1490 [[nodiscard]] bool appendSourceDataForToString(JSContext* cx,
1491 js::StringBuffer& buf);
1493 uint32_t lineno() const { return extent_.lineno; }
1494 uint32_t column() const { return extent_.column; }
1496 public:
1497 ImmutableScriptFlags immutableFlags() const { return immutableFlags_; }
1498 RO_IMMUTABLE_SCRIPT_FLAGS(immutableFlags_)
1499 RW_MUTABLE_SCRIPT_FLAGS(mutableFlags_)
1501 bool hasEnclosingScript() const { return warmUpData_.isEnclosingScript(); }
1502 BaseScript* enclosingScript() const {
1503 return warmUpData_.toEnclosingScript();
1505 void setEnclosingScript(BaseScript* enclosingScript);
1507 // Returns true is the script has an enclosing scope but no bytecode. It is
1508 // ready for delazification.
1509 // NOTE: The enclosing script must have been successfully compiled at some
1510 // point for the enclosing scope to exist. That script may have since been
1511 // GC'd, but we kept the scope live so we can still compile ourselves.
1512 bool isReadyForDelazification() const {
1513 return warmUpData_.isEnclosingScope();
1516 Scope* enclosingScope() const;
1517 void setEnclosingScope(Scope* enclosingScope);
1518 Scope* releaseEnclosingScope();
1520 bool hasJitScript() const { return warmUpData_.isJitScript(); }
1521 jit::JitScript* jitScript() const {
1522 MOZ_ASSERT(hasJitScript());
1523 return warmUpData_.toJitScript();
1525 jit::JitScript* maybeJitScript() const {
1526 return hasJitScript() ? jitScript() : nullptr;
1529 inline bool hasBaselineScript() const;
1530 inline bool hasIonScript() const;
1532 bool hasPrivateScriptData() const { return data_ != nullptr; }
1534 // Update data_ pointer while also informing GC MemoryUse tracking.
1535 void swapData(UniquePtr<PrivateScriptData>& other);
1537 mozilla::Span<const JS::GCCellPtr> gcthings() const {
1538 return data_ ? data_->gcthings() : mozilla::Span<JS::GCCellPtr>();
1541 // NOTE: This is only used to initialize a fresh script.
1542 mozilla::Span<JS::GCCellPtr> gcthingsForInit() {
1543 MOZ_ASSERT(!hasBytecode());
1544 return data_ ? data_->gcthings() : mozilla::Span<JS::GCCellPtr>();
1547 void setMemberInitializers(MemberInitializers memberInitializers) {
1548 MOZ_ASSERT(useMemberInitializers());
1549 MOZ_ASSERT(data_);
1550 data_->setMemberInitializers(memberInitializers);
1552 const MemberInitializers& getMemberInitializers() const {
1553 MOZ_ASSERT(data_);
1554 return data_->getMemberInitializers();
1557 SharedImmutableScriptData* sharedData() const { return sharedData_; }
1558 void initSharedData(SharedImmutableScriptData* data) {
1559 MOZ_ASSERT(sharedData_ == nullptr);
1560 sharedData_ = data;
1562 void freeSharedData() { sharedData_ = nullptr; }
1564 // NOTE: Script only has bytecode if JSScript::fullyInitFromStencil completes
1565 // successfully.
1566 bool hasBytecode() const {
1567 if (sharedData_) {
1568 MOZ_ASSERT(data_);
1569 MOZ_ASSERT(warmUpData_.isWarmUpCount() || warmUpData_.isJitScript());
1570 return true;
1572 return false;
1575 public:
1576 static const JS::TraceKind TraceKind = JS::TraceKind::Script;
1578 void traceChildren(JSTracer* trc);
1579 void finalize(JSFreeOp* fop);
1581 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
1582 return mallocSizeOf(data_);
1585 inline JSScript* asJSScript();
1587 // JIT accessors
1588 static constexpr size_t offsetOfJitCodeRaw() { return offsetOfHeaderPtr(); }
1589 static constexpr size_t offsetOfPrivateData() {
1590 return offsetof(BaseScript, data_);
1592 static constexpr size_t offsetOfSharedData() {
1593 return offsetof(BaseScript, sharedData_);
1595 static size_t offsetOfImmutableFlags() {
1596 static_assert(sizeof(ImmutableScriptFlags) == sizeof(uint32_t));
1597 return offsetof(BaseScript, immutableFlags_);
1599 static constexpr size_t offsetOfMutableFlags() {
1600 static_assert(sizeof(MutableScriptFlags) == sizeof(uint32_t));
1601 return offsetof(BaseScript, mutableFlags_);
1603 static constexpr size_t offsetOfWarmUpData() {
1604 return offsetof(BaseScript, warmUpData_);
1608 extern void SweepScriptData(JSRuntime* rt);
1610 } /* namespace js */
1612 class JSScript : public js::BaseScript {
1613 private:
1614 friend bool js::PrivateScriptData::InitFromStencil(
1615 JSContext* cx, js::HandleScript script,
1616 const js::frontend::CompilationAtomCache& atomCache,
1617 const js::frontend::CompilationStencil& stencil,
1618 js::frontend::CompilationGCOutput& gcOutput,
1619 const js::frontend::ScriptIndex scriptIndex);
1621 private:
1622 using js::BaseScript::BaseScript;
1624 public:
1625 static JSScript* Create(JSContext* cx, JS::Handle<JSFunction*> function,
1626 js::HandleScriptSourceObject sourceObject,
1627 const js::SourceExtent& extent,
1628 js::ImmutableScriptFlags flags);
1630 // NOTE: This should only be used while delazifying.
1631 static JSScript* CastFromLazy(js::BaseScript* lazy) {
1632 return static_cast<JSScript*>(lazy);
1635 // NOTE: If you use createPrivateScriptData directly instead of via
1636 // fullyInitFromStencil, you are responsible for notifying the debugger
1637 // after successfully creating the script.
1638 static bool createPrivateScriptData(JSContext* cx,
1639 JS::Handle<JSScript*> script,
1640 uint32_t ngcthings);
1642 public:
1643 static bool fullyInitFromStencil(
1644 JSContext* cx, const js::frontend::CompilationAtomCache& atomCache,
1645 const js::frontend::CompilationStencil& stencil,
1646 js::frontend::CompilationGCOutput& gcOutput, js::HandleScript script,
1647 const js::frontend::ScriptIndex scriptIndex);
1649 // Allocate a JSScript and initialize it with bytecode. This consumes
1650 // allocations within the stencil.
1651 static JSScript* fromStencil(JSContext* cx,
1652 js::frontend::CompilationAtomCache& atomCache,
1653 const js::frontend::CompilationStencil& stencil,
1654 js::frontend::CompilationGCOutput& gcOutput,
1655 js::frontend::ScriptIndex scriptIndex);
1657 #ifdef DEBUG
1658 private:
1659 // Assert that jump targets are within the code array of the script.
1660 void assertValidJumpTargets() const;
1661 #endif
1663 public:
1664 js::ImmutableScriptData* immutableScriptData() const {
1665 return sharedData_->get();
1668 // Script bytecode is immutable after creation.
1669 jsbytecode* code() const {
1670 if (!sharedData_) {
1671 return nullptr;
1673 return immutableScriptData()->code();
1676 bool hasForceInterpreterOp() const {
1677 // JSOp::ForceInterpreter, if present, must be the first op.
1678 MOZ_ASSERT(length() >= 1);
1679 return JSOp(*code()) == JSOp::ForceInterpreter;
1682 js::AllBytecodesIterable allLocations() {
1683 return js::AllBytecodesIterable(this);
1686 js::BytecodeLocation location() { return js::BytecodeLocation(this, code()); }
1688 size_t length() const {
1689 MOZ_ASSERT(sharedData_);
1690 return immutableScriptData()->codeLength();
1693 jsbytecode* codeEnd() const { return code() + length(); }
1695 jsbytecode* lastPC() const {
1696 jsbytecode* pc = codeEnd() - js::JSOpLength_RetRval;
1697 MOZ_ASSERT(JSOp(*pc) == JSOp::RetRval);
1698 return pc;
1701 // Note: ArgBytes is optional, but if specified then containsPC will also
1702 // check that the opcode arguments are in bounds.
1703 template <size_t ArgBytes = 0>
1704 bool containsPC(const jsbytecode* pc) const {
1705 MOZ_ASSERT_IF(ArgBytes,
1706 js::GetBytecodeLength(pc) == sizeof(jsbytecode) + ArgBytes);
1707 const jsbytecode* lastByte = pc + ArgBytes;
1708 return pc >= code() && lastByte < codeEnd();
1710 template <typename ArgType>
1711 bool containsPC(const jsbytecode* pc) const {
1712 return containsPC<sizeof(ArgType)>(pc);
1715 bool contains(const js::BytecodeLocation& loc) const {
1716 return containsPC(loc.toRawBytecode());
1719 size_t pcToOffset(const jsbytecode* pc) const {
1720 MOZ_ASSERT(containsPC(pc));
1721 return size_t(pc - code());
1724 jsbytecode* offsetToPC(size_t offset) const {
1725 MOZ_ASSERT(offset < length());
1726 return code() + offset;
1729 size_t mainOffset() const { return immutableScriptData()->mainOffset; }
1731 // The fixed part of a stack frame is comprised of vars (in function and
1732 // module code) and block-scoped locals (in all kinds of code).
1733 size_t nfixed() const { return immutableScriptData()->nfixed; }
1735 // Number of fixed slots reserved for slots that are always live. Only
1736 // nonzero for function or module code.
1737 size_t numAlwaysLiveFixedSlots() const;
1739 // Calculate the number of fixed slots that are live at a particular bytecode.
1740 size_t calculateLiveFixed(jsbytecode* pc);
1742 size_t nslots() const { return immutableScriptData()->nslots; }
1744 unsigned numArgs() const;
1746 inline js::Shape* initialEnvironmentShape() const;
1748 bool functionHasParameterExprs() const;
1750 bool functionAllowsParameterRedeclaration() const {
1751 // Parameter redeclaration is only allowed for non-strict functions with
1752 // simple parameter lists, which are neither arrow nor method functions. We
1753 // don't have a flag at hand to test the function kind, but we can still
1754 // test if the function is non-strict and has a simple parameter list by
1755 // checking |hasMappedArgsObj()|. (Mapped arguments objects are only
1756 // created for non-strict functions with simple parameter lists.)
1757 return hasMappedArgsObj();
1760 size_t numICEntries() const { return immutableScriptData()->numICEntries; }
1762 size_t funLength() const { return immutableScriptData()->funLength; }
1764 void cacheForEval() {
1765 MOZ_ASSERT(isForEval());
1766 // IsEvalCacheCandidate will make sure that there's nothing in this
1767 // script that would prevent reexecution even if isRunOnce is
1768 // true. So just pretend like we never ran this script.
1769 clearFlag(MutableFlags::HasRunOnce);
1773 * Arguments access (via JSOp::*Arg* opcodes) must access the canonical
1774 * location for the argument. If an arguments object exists AND it's mapped
1775 * ('arguments' aliases formals), then all access must go through the
1776 * arguments object. Otherwise, the local slot is the canonical location for
1777 * the arguments. Note: if a formal is aliased through the scope chain, then
1778 * script->formalIsAliased and JSOp::*Arg* opcodes won't be emitted at all.
1780 bool argsObjAliasesFormals() const {
1781 return needsArgsObj() && hasMappedArgsObj();
1784 void updateJitCodeRaw(JSRuntime* rt);
1786 js::ModuleObject* module() const;
1788 bool isGlobalCode() const;
1790 // Returns true if the script may read formal arguments on the stack
1791 // directly, via lazy arguments or a rest parameter.
1792 bool mayReadFrameArgsDirectly();
1794 static JSLinearString* sourceData(JSContext* cx, JS::HandleScript script);
1796 #ifdef MOZ_VTUNE
1797 // Unique Method ID passed to the VTune profiler. Allows attribution of
1798 // different jitcode to the same source script.
1799 uint32_t vtuneMethodID();
1800 #endif
1802 public:
1803 /* Return whether this is a 'direct eval' script in a function scope. */
1804 bool isDirectEvalInFunction() const;
1807 * Return whether this script is a top-level script.
1809 * If we evaluate some code which contains a syntax error, then we might
1810 * produce a JSScript which has no associated bytecode. Testing with
1811 * |code()| filters out this kind of scripts.
1813 * If this script has a function associated to it, then it is not the
1814 * top-level of a file.
1816 bool isTopLevel() { return code() && !isFunction(); }
1818 /* Ensure the script has a JitScript. */
1819 inline bool ensureHasJitScript(JSContext* cx, js::jit::AutoKeepJitScripts&);
1821 void maybeReleaseJitScript(JSFreeOp* fop);
1822 void releaseJitScript(JSFreeOp* fop);
1823 void releaseJitScriptOnFinalize(JSFreeOp* fop);
1825 inline js::jit::BaselineScript* baselineScript() const;
1826 inline js::jit::IonScript* ionScript() const;
1828 inline bool isIonCompilingOffThread() const;
1829 inline bool canIonCompile() const;
1830 inline void disableIon();
1832 inline bool canBaselineCompile() const;
1833 inline void disableBaselineCompile();
1835 inline js::GlobalObject& global() const;
1836 inline bool hasGlobal(const js::GlobalObject* global) const;
1837 js::GlobalObject& uninlinedGlobal() const;
1839 js::GCThingIndex bodyScopeIndex() const {
1840 return immutableScriptData()->bodyScopeIndex;
1843 js::Scope* bodyScope() const { return getScope(bodyScopeIndex()); }
1845 js::Scope* outermostScope() const {
1846 // The body scope may not be the outermost scope in the script when
1847 // the decl env scope is present.
1848 return getScope(js::GCThingIndex::outermostScopeIndex());
1851 bool functionHasExtraBodyVarScope() const {
1852 bool res = BaseScript::functionHasExtraBodyVarScope();
1853 MOZ_ASSERT_IF(res, functionHasParameterExprs());
1854 return res;
1857 js::VarScope* functionExtraBodyVarScope() const;
1859 bool needsBodyEnvironment() const;
1861 inline js::LexicalScope* maybeNamedLambdaScope() const;
1863 // Drop script data and reset warmUpData to reference enclosing scope.
1864 void relazify(JSRuntime* rt);
1866 private:
1867 bool createJitScript(JSContext* cx);
1869 bool shareScriptData(JSContext* cx);
1871 public:
1872 inline uint32_t getWarmUpCount() const;
1873 inline void incWarmUpCounter(uint32_t amount = 1);
1874 inline void resetWarmUpCounterForGC();
1876 void resetWarmUpCounterToDelayIonCompilation();
1878 unsigned getWarmUpResetCount() const {
1879 constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK);
1880 return mutableFlags_ & MASK;
1882 void incWarmUpResetCounter() {
1883 constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK);
1884 uint32_t newCount = getWarmUpResetCount() + 1;
1885 if (newCount <= MASK) {
1886 mutableFlags_ &= ~MASK;
1887 mutableFlags_ |= newCount;
1890 void resetWarmUpResetCounter() {
1891 constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK);
1892 mutableFlags_ &= ~MASK;
1895 public:
1896 bool initScriptCounts(JSContext* cx);
1897 js::ScriptCounts& getScriptCounts();
1898 js::PCCounts* maybeGetPCCounts(jsbytecode* pc);
1899 const js::PCCounts* maybeGetThrowCounts(jsbytecode* pc);
1900 js::PCCounts* getThrowCounts(jsbytecode* pc);
1901 uint64_t getHitCount(jsbytecode* pc);
1902 void addIonCounts(js::jit::IonScriptCounts* ionCounts);
1903 js::jit::IonScriptCounts* getIonCounts();
1904 void releaseScriptCounts(js::ScriptCounts* counts);
1905 void destroyScriptCounts();
1906 void resetScriptCounts();
1908 jsbytecode* main() const { return code() + mainOffset(); }
1910 js::BytecodeLocation mainLocation() const {
1911 return js::BytecodeLocation(this, main());
1914 js::BytecodeLocation endLocation() const {
1915 return js::BytecodeLocation(this, codeEnd());
1918 js::BytecodeLocation offsetToLocation(uint32_t offset) const {
1919 return js::BytecodeLocation(this, offsetToPC(offset));
1922 void addSizeOfJitScript(mozilla::MallocSizeOf mallocSizeOf,
1923 size_t* sizeOfJitScript,
1924 size_t* sizeOfBaselineFallbackStubs) const;
1926 mozilla::Span<const js::TryNote> trynotes() const {
1927 return immutableScriptData()->tryNotes();
1930 mozilla::Span<const js::ScopeNote> scopeNotes() const {
1931 return immutableScriptData()->scopeNotes();
1934 mozilla::Span<const uint32_t> resumeOffsets() const {
1935 return immutableScriptData()->resumeOffsets();
1938 uint32_t tableSwitchCaseOffset(jsbytecode* pc, uint32_t caseIndex) const {
1939 MOZ_ASSERT(containsPC(pc));
1940 MOZ_ASSERT(JSOp(*pc) == JSOp::TableSwitch);
1941 uint32_t firstResumeIndex = GET_RESUMEINDEX(pc + 3 * JUMP_OFFSET_LEN);
1942 return resumeOffsets()[firstResumeIndex + caseIndex];
1944 jsbytecode* tableSwitchCasePC(jsbytecode* pc, uint32_t caseIndex) const {
1945 return offsetToPC(tableSwitchCaseOffset(pc, caseIndex));
1948 bool hasLoops();
1950 uint32_t numNotes() const {
1951 MOZ_ASSERT(sharedData_);
1952 return immutableScriptData()->noteLength();
1954 js::SrcNote* notes() const {
1955 MOZ_ASSERT(sharedData_);
1956 return immutableScriptData()->notes();
1959 JSString* getString(js::GCThingIndex index) const {
1960 return &gcthings()[index].as<JSString>();
1963 JSString* getString(jsbytecode* pc) const {
1964 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
1965 MOZ_ASSERT(js::JOF_OPTYPE((JSOp)*pc) == JOF_STRING);
1966 return getString(GET_GCTHING_INDEX(pc));
1969 JSAtom* getAtom(js::GCThingIndex index) const {
1970 return &gcthings()[index].as<JSString>().asAtom();
1973 JSAtom* getAtom(jsbytecode* pc) const {
1974 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
1975 MOZ_ASSERT(js::JOF_OPTYPE((JSOp)*pc) == JOF_ATOM);
1976 return getAtom(GET_GCTHING_INDEX(pc));
1979 js::PropertyName* getName(js::GCThingIndex index) {
1980 return getAtom(index)->asPropertyName();
1983 js::PropertyName* getName(jsbytecode* pc) const {
1984 return getAtom(pc)->asPropertyName();
1987 JSObject* getObject(js::GCThingIndex index) const {
1988 MOZ_ASSERT(gcthings()[index].asCell()->isTenured());
1989 return &gcthings()[index].as<JSObject>();
1992 JSObject* getObject(const jsbytecode* pc) const {
1993 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
1994 return getObject(GET_GCTHING_INDEX(pc));
1997 js::Shape* getShape(js::GCThingIndex index) const {
1998 return &gcthings()[index].as<js::Shape>();
2001 js::Shape* getShape(const jsbytecode* pc) const {
2002 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
2003 return getShape(GET_GCTHING_INDEX(pc));
2006 js::Scope* getScope(js::GCThingIndex index) const {
2007 return &gcthings()[index].as<js::Scope>();
2010 js::Scope* getScope(jsbytecode* pc) const {
2011 // This method is used to get a scope directly using a JSOp with an
2012 // index. To search through ScopeNotes to look for a Scope using pc,
2013 // use lookupScope.
2014 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
2015 MOZ_ASSERT(js::JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPE,
2016 "Did you mean to use lookupScope(pc)?");
2017 return getScope(GET_GCTHING_INDEX(pc));
2020 inline JSFunction* getFunction(js::GCThingIndex index) const;
2021 inline JSFunction* getFunction(jsbytecode* pc) const;
2023 inline js::RegExpObject* getRegExp(js::GCThingIndex index) const;
2024 inline js::RegExpObject* getRegExp(jsbytecode* pc) const;
2026 js::BigInt* getBigInt(js::GCThingIndex index) const {
2027 MOZ_ASSERT(gcthings()[index].asCell()->isTenured());
2028 return &gcthings()[index].as<js::BigInt>();
2031 js::BigInt* getBigInt(jsbytecode* pc) const {
2032 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
2033 MOZ_ASSERT(js::JOF_OPTYPE(JSOp(*pc)) == JOF_BIGINT);
2034 return getBigInt(GET_GCTHING_INDEX(pc));
2037 // The following 3 functions find the static scope just before the
2038 // execution of the instruction pointed to by pc.
2040 js::Scope* lookupScope(const jsbytecode* pc) const;
2042 js::Scope* innermostScope(const jsbytecode* pc) const;
2043 js::Scope* innermostScope() const { return innermostScope(main()); }
2046 * The isEmpty method tells whether this script has code that computes any
2047 * result (not return value, result AKA normal completion value) other than
2048 * JSVAL_VOID, or any other effects.
2050 bool isEmpty() const {
2051 if (length() > 3) {
2052 return false;
2055 jsbytecode* pc = code();
2056 if (noScriptRval() && JSOp(*pc) == JSOp::False) {
2057 ++pc;
2059 return JSOp(*pc) == JSOp::RetRval;
2062 bool formalIsAliased(unsigned argSlot);
2063 bool anyFormalIsForwarded();
2064 bool formalLivesInArgumentsObject(unsigned argSlot);
2066 // See comment above 'debugMode' in Realm.h for explanation of
2067 // invariants of debuggee compartments, scripts, and frames.
2068 inline bool isDebuggee() const;
2070 // Create an allocation site associated with this script/JitScript to track
2071 // nursery allocations.
2072 js::gc::AllocSite* createAllocSite();
2074 // A helper class to prevent relazification of the given function's script
2075 // while it's holding on to it. This class automatically roots the script.
2076 class AutoDelazify;
2077 friend class AutoDelazify;
2079 class AutoDelazify {
2080 JS::RootedScript script_;
2081 JSContext* cx_;
2082 bool oldAllowRelazify_ = false;
2084 public:
2085 explicit AutoDelazify(JSContext* cx, JS::HandleFunction fun = nullptr)
2086 : script_(cx), cx_(cx) {
2087 holdScript(fun);
2090 ~AutoDelazify() { dropScript(); }
2092 void operator=(JS::HandleFunction fun) {
2093 dropScript();
2094 holdScript(fun);
2097 operator JS::HandleScript() const { return script_; }
2098 explicit operator bool() const { return script_; }
2100 private:
2101 void holdScript(JS::HandleFunction fun);
2102 void dropScript();
2106 namespace js {
2108 struct ScriptAndCounts {
2109 /* This structure is stored and marked from the JSRuntime. */
2110 JSScript* script;
2111 ScriptCounts scriptCounts;
2113 inline explicit ScriptAndCounts(JSScript* script);
2114 inline ScriptAndCounts(ScriptAndCounts&& sac);
2116 const PCCounts* maybeGetPCCounts(jsbytecode* pc) const {
2117 return scriptCounts.maybeGetPCCounts(script->pcToOffset(pc));
2119 const PCCounts* maybeGetThrowCounts(jsbytecode* pc) const {
2120 return scriptCounts.maybeGetThrowCounts(script->pcToOffset(pc));
2123 jit::IonScriptCounts* getIonCounts() const { return scriptCounts.ionCounts_; }
2125 void trace(JSTracer* trc) {
2126 TraceRoot(trc, &script, "ScriptAndCounts::script");
2130 extern JS::UniqueChars FormatIntroducedFilename(JSContext* cx,
2131 const char* filename,
2132 unsigned lineno,
2133 const char* introducer);
2135 struct GSNCache;
2137 const js::SrcNote* GetSrcNote(GSNCache& cache, JSScript* script,
2138 jsbytecode* pc);
2140 extern const js::SrcNote* GetSrcNote(JSContext* cx, JSScript* script,
2141 jsbytecode* pc);
2143 extern jsbytecode* LineNumberToPC(JSScript* script, unsigned lineno);
2145 extern JS_PUBLIC_API unsigned GetScriptLineExtent(JSScript* script);
2147 #ifdef JS_CACHEIR_SPEW
2148 void maybeUpdateWarmUpCount(JSScript* script);
2149 void maybeSpewScriptFinalWarmUpCount(JSScript* script);
2150 #endif
2152 } /* namespace js */
2154 namespace js {
2156 extern unsigned PCToLineNumber(JSScript* script, jsbytecode* pc,
2157 unsigned* columnp = nullptr);
2159 extern unsigned PCToLineNumber(unsigned startLine, unsigned startCol,
2160 SrcNote* notes, jsbytecode* code, jsbytecode* pc,
2161 unsigned* columnp = nullptr);
2164 * This function returns the file and line number of the script currently
2165 * executing on cx. If there is no current script executing on cx (e.g., a
2166 * native called directly through JSAPI (e.g., by setTimeout)), nullptr and 0
2167 * are returned as the file and line.
2169 extern void DescribeScriptedCallerForCompilation(
2170 JSContext* cx, MutableHandleScript maybeScript, const char** file,
2171 unsigned* linenop, uint32_t* pcOffset, bool* mutedErrors);
2174 * Like DescribeScriptedCallerForCompilation, but this function avoids looking
2175 * up the script/pc and the full linear scan to compute line number.
2177 extern void DescribeScriptedCallerForDirectEval(
2178 JSContext* cx, HandleScript script, jsbytecode* pc, const char** file,
2179 unsigned* linenop, uint32_t* pcOffset, bool* mutedErrors);
2181 bool CheckCompileOptionsMatch(const JS::ReadOnlyCompileOptions& options,
2182 js::ImmutableScriptFlags flags,
2183 bool isMultiDecode);
2185 void FillImmutableFlagsFromCompileOptionsForTopLevel(
2186 const JS::ReadOnlyCompileOptions& options, js::ImmutableScriptFlags& flags);
2188 void FillImmutableFlagsFromCompileOptionsForFunction(
2189 const JS::ReadOnlyCompileOptions& options, js::ImmutableScriptFlags& flags);
2191 } /* namespace js */
2193 namespace JS {
2195 template <>
2196 struct GCPolicy<js::ScriptLCovEntry>
2197 : public IgnoreGCPolicy<js::ScriptLCovEntry> {};
2199 #ifdef JS_CACHEIR_SPEW
2200 template <>
2201 struct GCPolicy<js::ScriptFinalWarmUpCountEntry>
2202 : public IgnoreGCPolicy<js::ScriptFinalWarmUpCountEntry> {};
2203 #endif
2205 namespace ubi {
2207 template <>
2208 class Concrete<JSScript> : public Concrete<js::BaseScript> {};
2210 } // namespace ubi
2211 } // namespace JS
2213 #endif /* vm_JSScript_h */