no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / js / src / vm / JSScript.h
blob7b7ab6dc05c59adaa12cf59b51b4a33a81101ccd
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"
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 "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
46 namespace JS {
47 struct ScriptSourceInfo;
48 template <typename UnitT>
49 class SourceText;
50 } // namespace JS
52 namespace js {
54 class FrontendContext;
55 class ScriptSource;
57 class VarScope;
58 class LexicalScope;
60 class JS_PUBLIC_API Sprinter;
62 namespace coverage {
63 class LCovSource;
64 } // namespace coverage
66 namespace gc {
67 class AllocSite;
68 } // namespace gc
70 namespace jit {
71 class AutoKeepJitScripts;
72 class BaselineScript;
73 class IonScript;
74 struct IonScriptCounts;
75 class JitScript;
76 } // namespace jit
78 class ModuleObject;
79 class RegExpObject;
80 class SourceCompressionTask;
81 class Shape;
82 class SrcNote;
83 class DebugScript;
85 namespace frontend {
86 struct CompilationStencil;
87 struct ExtensibleCompilationStencil;
88 struct CompilationGCOutput;
89 struct CompilationStencilMerger;
90 class StencilXDR;
91 } // namespace frontend
93 class ScriptCounts {
94 public:
95 using PCCountsVector = mozilla::Vector<PCCounts, 0, SystemAllocPolicy>;
97 inline ScriptCounts();
98 inline explicit ScriptCounts(PCCountsVector&& jumpTargets);
99 inline ScriptCounts(ScriptCounts&& src);
100 inline ~ScriptCounts();
102 inline ScriptCounts& operator=(ScriptCounts&& src);
104 // Return the counter used to count the number of visits. Returns null if
105 // the element is not found.
106 PCCounts* maybeGetPCCounts(size_t offset);
107 const PCCounts* maybeGetPCCounts(size_t offset) const;
109 // PCCounts are stored at jump-target offsets. This function looks for the
110 // previous PCCount which is in the same basic block as the current offset.
111 PCCounts* getImmediatePrecedingPCCounts(size_t offset);
113 // Return the counter used to count the number of throws. Returns null if
114 // the element is not found.
115 const PCCounts* maybeGetThrowCounts(size_t offset) const;
117 // Throw counts are stored at the location of each throwing
118 // instruction. This function looks for the previous throw count.
120 // Note: if the offset of the returned count is higher than the offset of
121 // the immediate preceding PCCount, then this throw happened in the same
122 // basic block.
123 const PCCounts* getImmediatePrecedingThrowCounts(size_t offset) const;
125 // Return the counter used to count the number of throws. Allocate it if
126 // none exists yet. Returns null if the allocation failed.
127 PCCounts* getThrowCounts(size_t offset);
129 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
131 bool traceWeak(JSTracer* trc) { return true; }
133 private:
134 friend class ::JSScript;
135 friend struct ScriptAndCounts;
137 // This sorted array is used to map an offset to the number of times a
138 // branch got visited.
139 PCCountsVector pcCounts_;
141 // This sorted vector is used to map an offset to the number of times an
142 // instruction throw.
143 PCCountsVector throwCounts_;
145 // Information about any Ion compilations for the script.
146 jit::IonScriptCounts* ionCounts_;
149 // The key of these side-table hash maps are intentionally not traced GC
150 // references to JSScript. Instead, we use bare pointers and manually fix up
151 // when objects could have moved (see Zone::fixupScriptMapsAfterMovingGC) and
152 // remove when the realm is destroyed (see Zone::clearScriptCounts and
153 // Zone::clearScriptNames). They essentially behave as weak references, except
154 // that the references are not cleared early by the GC. They must be non-strong
155 // references because the tables are kept at the Zone level and otherwise the
156 // table keys would keep scripts alive, thus keeping Realms alive, beyond their
157 // expected lifetimes. However, We do not use actual weak references (e.g. as
158 // used by WeakMap tables provided in gc/WeakMap.h) because they would be
159 // collected before the calls to the JSScript::finalize function which are used
160 // to aggregate code coverage results on the realm.
162 // Note carefully, however, that there is an exceptional case for which we *do*
163 // want the JSScripts to be strong references (and thus traced): when the
164 // --dump-bytecode command line option or the PCCount JSFriend API is used,
165 // then the scripts for all counts must remain alive. See
166 // Zone::traceScriptTableRoots() for more details.
168 // TODO: Clean this up by either aggregating coverage results in some other
169 // way, or by tweaking sweep ordering.
170 using UniqueScriptCounts = js::UniquePtr<ScriptCounts>;
171 using ScriptCountsMap =
172 GCRekeyableHashMap<HeapPtr<BaseScript*>, UniqueScriptCounts,
173 DefaultHasher<HeapPtr<BaseScript*>>, SystemAllocPolicy>;
175 // The 'const char*' for the function name is a pointer within the LCovSource's
176 // LifoAlloc and will be discarded at the same time.
177 using ScriptLCovEntry = std::tuple<coverage::LCovSource*, const char*>;
178 using ScriptLCovMap =
179 GCRekeyableHashMap<HeapPtr<BaseScript*>, ScriptLCovEntry,
180 DefaultHasher<HeapPtr<BaseScript*>>, SystemAllocPolicy>;
182 #ifdef MOZ_VTUNE
183 using ScriptVTuneIdMap =
184 GCRekeyableHashMap<HeapPtr<BaseScript*>, uint32_t,
185 DefaultHasher<HeapPtr<BaseScript*>>, SystemAllocPolicy>;
186 #endif
187 #ifdef JS_CACHEIR_SPEW
188 using ScriptFinalWarmUpCountEntry = std::tuple<uint32_t, SharedImmutableString>;
189 using ScriptFinalWarmUpCountMap =
190 GCRekeyableHashMap<HeapPtr<BaseScript*>, ScriptFinalWarmUpCountEntry,
191 DefaultHasher<HeapPtr<BaseScript*>>, SystemAllocPolicy>;
192 #endif
194 // As we execute JS sources that used lazy parsing, we may generate additional
195 // bytecode that we would like to include in caches if they are being used.
196 // There is a dependency cycle between JSScript / ScriptSource /
197 // CompilationStencil for this scenario so introduce this smart-ptr wrapper to
198 // avoid needing the full details of the stencil-merger in this file.
199 class StencilIncrementalEncoderPtr {
200 public:
201 frontend::CompilationStencilMerger* merger_ = nullptr;
203 StencilIncrementalEncoderPtr() = default;
204 ~StencilIncrementalEncoderPtr() { reset(); }
206 bool hasEncoder() const { return bool(merger_); }
208 void reset();
210 bool setInitial(JSContext* cx,
211 UniquePtr<frontend::ExtensibleCompilationStencil>&& initial);
213 bool addDelazification(JSContext* cx,
214 const frontend::CompilationStencil& delazification);
217 struct ScriptSourceChunk {
218 ScriptSource* ss = nullptr;
219 uint32_t chunk = 0;
221 ScriptSourceChunk() = default;
223 ScriptSourceChunk(ScriptSource* ss, uint32_t chunk) : ss(ss), chunk(chunk) {
224 MOZ_ASSERT(valid());
227 bool valid() const { return ss != nullptr; }
229 bool operator==(const ScriptSourceChunk& other) const {
230 return ss == other.ss && chunk == other.chunk;
234 struct ScriptSourceChunkHasher {
235 using Lookup = ScriptSourceChunk;
237 static HashNumber hash(const ScriptSourceChunk& ssc) {
238 return mozilla::AddToHash(DefaultHasher<ScriptSource*>::hash(ssc.ss),
239 ssc.chunk);
241 static bool match(const ScriptSourceChunk& c1, const ScriptSourceChunk& c2) {
242 return c1 == c2;
246 template <typename Unit>
247 using EntryUnits = mozilla::UniquePtr<Unit[], JS::FreePolicy>;
249 // The uncompressed source cache contains *either* UTF-8 source data *or*
250 // UTF-16 source data. ScriptSourceChunk implies a ScriptSource that
251 // contains either UTF-8 data or UTF-16 data, so the nature of the key to
252 // Map below indicates how each SourceData ought to be interpreted.
253 using SourceData = mozilla::UniquePtr<void, JS::FreePolicy>;
255 template <typename Unit>
256 inline SourceData ToSourceData(EntryUnits<Unit> chars) {
257 static_assert(std::is_same_v<SourceData::DeleterType,
258 typename EntryUnits<Unit>::DeleterType>,
259 "EntryUnits and SourceData must share the same deleter "
260 "type, that need not know the type of the data being freed, "
261 "for the upcast below to be safe");
262 return SourceData(chars.release());
265 class UncompressedSourceCache {
266 using Map = HashMap<ScriptSourceChunk, SourceData, ScriptSourceChunkHasher,
267 SystemAllocPolicy>;
269 public:
270 // Hold an entry in the source data cache and prevent it from being purged on
271 // GC.
272 class AutoHoldEntry {
273 UncompressedSourceCache* cache_ = nullptr;
274 ScriptSourceChunk sourceChunk_ = {};
275 SourceData data_ = nullptr;
277 public:
278 explicit AutoHoldEntry() = default;
280 ~AutoHoldEntry() {
281 if (cache_) {
282 MOZ_ASSERT(sourceChunk_.valid());
283 cache_->releaseEntry(*this);
287 template <typename Unit>
288 void holdUnits(EntryUnits<Unit> units) {
289 MOZ_ASSERT(!cache_);
290 MOZ_ASSERT(!sourceChunk_.valid());
291 MOZ_ASSERT(!data_);
293 data_ = ToSourceData(std::move(units));
296 private:
297 void holdEntry(UncompressedSourceCache* cache,
298 const ScriptSourceChunk& sourceChunk) {
299 // Initialise the holder for a specific cache and script source.
300 // This will hold on to the cached source chars in the event that
301 // the cache is purged.
302 MOZ_ASSERT(!cache_);
303 MOZ_ASSERT(!sourceChunk_.valid());
304 MOZ_ASSERT(!data_);
306 cache_ = cache;
307 sourceChunk_ = sourceChunk;
310 void deferDelete(SourceData data) {
311 // Take ownership of source chars now the cache is being purged. Remove
312 // our reference to the ScriptSource which might soon be destroyed.
313 MOZ_ASSERT(cache_);
314 MOZ_ASSERT(sourceChunk_.valid());
315 MOZ_ASSERT(!data_);
317 cache_ = nullptr;
318 sourceChunk_ = ScriptSourceChunk();
320 data_ = std::move(data);
323 const ScriptSourceChunk& sourceChunk() const { return sourceChunk_; }
324 friend class UncompressedSourceCache;
327 private:
328 UniquePtr<Map> map_ = nullptr;
329 AutoHoldEntry* holder_ = nullptr;
331 public:
332 UncompressedSourceCache() = default;
334 template <typename Unit>
335 const Unit* lookup(const ScriptSourceChunk& ssc, AutoHoldEntry& asp);
337 bool put(const ScriptSourceChunk& ssc, SourceData data, AutoHoldEntry& asp);
339 void purge();
341 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
343 private:
344 void holdEntry(AutoHoldEntry& holder, const ScriptSourceChunk& ssc);
345 void releaseEntry(AutoHoldEntry& holder);
348 template <typename Unit>
349 struct SourceTypeTraits;
351 template <>
352 struct SourceTypeTraits<mozilla::Utf8Unit> {
353 using CharT = char;
354 using SharedImmutableString = js::SharedImmutableString;
356 static const mozilla::Utf8Unit* units(const SharedImmutableString& string) {
357 // Casting |char| data to |Utf8Unit| is safe because |Utf8Unit|
358 // contains a |char|. See the long comment in |Utf8Unit|'s definition.
359 return reinterpret_cast<const mozilla::Utf8Unit*>(string.chars());
362 static char* toString(const mozilla::Utf8Unit* units) {
363 auto asUnsigned =
364 const_cast<unsigned char*>(mozilla::Utf8AsUnsignedChars(units));
365 return reinterpret_cast<char*>(asUnsigned);
368 static UniqueChars toCacheable(EntryUnits<mozilla::Utf8Unit> str) {
369 // The cache only stores strings of |char| or |char16_t|, and right now
370 // it seems best not to gunk up the cache with |Utf8Unit| too. So
371 // cache |Utf8Unit| strings by interpreting them as |char| strings.
372 char* chars = toString(str.release());
373 return UniqueChars(chars);
377 template <>
378 struct SourceTypeTraits<char16_t> {
379 using CharT = char16_t;
380 using SharedImmutableString = js::SharedImmutableTwoByteString;
382 static const char16_t* units(const SharedImmutableString& string) {
383 return string.chars();
386 static char16_t* toString(const char16_t* units) {
387 return const_cast<char16_t*>(units);
390 static UniqueTwoByteChars toCacheable(EntryUnits<char16_t> str) {
391 return UniqueTwoByteChars(std::move(str));
395 // Synchronously compress the source of |script|, for testing purposes.
396 [[nodiscard]] extern bool SynchronouslyCompressSource(
397 JSContext* cx, JS::Handle<BaseScript*> script);
399 // [SMDOC] ScriptSource
401 // This class abstracts over the source we used to compile from. The current
402 // representation may transition to different modes in order to save memory.
403 // Abstractly the source may be one of UTF-8 or UTF-16. The data itself may be
404 // unavailable, retrieveable-using-source-hook, compressed, or uncompressed. If
405 // source is retrieved or decompressed for use, we may update the ScriptSource
406 // to hold the result.
407 class ScriptSource {
408 // NOTE: While ScriptSources may be compressed off thread, they are only
409 // modified by the main thread, and all members are always safe to access
410 // on the main thread.
412 friend class SourceCompressionTask;
413 friend bool SynchronouslyCompressSource(JSContext* cx,
414 JS::Handle<BaseScript*> script);
416 friend class frontend::StencilXDR;
418 private:
419 // Common base class of the templated variants of PinnedUnits<T>.
420 class PinnedUnitsBase {
421 protected:
422 ScriptSource* source_;
424 explicit PinnedUnitsBase(ScriptSource* source) : source_(source) {}
426 void addReader();
428 template <typename Unit>
429 void removeReader();
432 public:
433 // Any users that wish to manipulate the char buffer of the ScriptSource
434 // needs to do so via PinnedUnits for GC safety. A GC may compress
435 // ScriptSources. If the source were initially uncompressed, then any raw
436 // pointers to the char buffer would now point to the freed, uncompressed
437 // chars. This is analogous to Rooted.
438 template <typename Unit>
439 class PinnedUnits : public PinnedUnitsBase {
440 const Unit* units_;
442 public:
443 PinnedUnits(JSContext* cx, ScriptSource* source,
444 UncompressedSourceCache::AutoHoldEntry& holder, size_t begin,
445 size_t len);
447 ~PinnedUnits();
449 const Unit* get() const { return units_; }
451 const typename SourceTypeTraits<Unit>::CharT* asChars() const {
452 return SourceTypeTraits<Unit>::toString(get());
456 template <typename Unit>
457 class PinnedUnitsIfUncompressed : public PinnedUnitsBase {
458 const Unit* units_;
460 public:
461 PinnedUnitsIfUncompressed(ScriptSource* source, size_t begin, size_t len);
463 ~PinnedUnitsIfUncompressed();
465 const Unit* get() const { return units_; }
467 const typename SourceTypeTraits<Unit>::CharT* asChars() const {
468 return SourceTypeTraits<Unit>::toString(get());
472 private:
473 // Missing source text that isn't retrievable using the source hook. (All
474 // ScriptSources initially begin in this state. Users that are compiling
475 // source text will overwrite |data| to store a different state.)
476 struct Missing {};
478 // Source that can be retrieved using the registered source hook. |Unit|
479 // records the source type so that source-text coordinates in functions and
480 // scripts that depend on this |ScriptSource| are correct.
481 template <typename Unit>
482 struct Retrievable {
483 // The source hook and script URL required to retrieve source are stored
484 // elsewhere, so nothing is needed here. It'd be better hygiene to store
485 // something source-hook-like in each |ScriptSource| that needs it, but that
486 // requires reimagining a source-hook API that currently depends on source
487 // hooks being uniquely-owned pointers...
490 // Uncompressed source text. Templates distinguish if we are interconvertable
491 // to |Retrievable| or not.
492 template <typename Unit>
493 class UncompressedData {
494 typename SourceTypeTraits<Unit>::SharedImmutableString string_;
496 public:
497 explicit UncompressedData(
498 typename SourceTypeTraits<Unit>::SharedImmutableString str)
499 : string_(std::move(str)) {}
501 const Unit* units() const { return SourceTypeTraits<Unit>::units(string_); }
503 size_t length() const { return string_.length(); }
506 template <typename Unit, SourceRetrievable CanRetrieve>
507 class Uncompressed : public UncompressedData<Unit> {
508 using Base = UncompressedData<Unit>;
510 public:
511 using Base::Base;
514 // Compressed source text. Templates distinguish if we are interconvertable
515 // to |Retrievable| or not.
516 template <typename Unit>
517 struct CompressedData {
518 // Single-byte compressed text, regardless whether the original text
519 // was single-byte or two-byte.
520 SharedImmutableString raw;
521 size_t uncompressedLength;
523 CompressedData(SharedImmutableString raw, size_t uncompressedLength)
524 : raw(std::move(raw)), uncompressedLength(uncompressedLength) {}
527 template <typename Unit, SourceRetrievable CanRetrieve>
528 struct Compressed : public CompressedData<Unit> {
529 using Base = CompressedData<Unit>;
531 public:
532 using Base::Base;
535 // The set of currently allowed encoding modes.
536 using SourceType =
537 mozilla::Variant<Compressed<mozilla::Utf8Unit, SourceRetrievable::Yes>,
538 Uncompressed<mozilla::Utf8Unit, SourceRetrievable::Yes>,
539 Compressed<mozilla::Utf8Unit, SourceRetrievable::No>,
540 Uncompressed<mozilla::Utf8Unit, SourceRetrievable::No>,
541 Compressed<char16_t, SourceRetrievable::Yes>,
542 Uncompressed<char16_t, SourceRetrievable::Yes>,
543 Compressed<char16_t, SourceRetrievable::No>,
544 Uncompressed<char16_t, SourceRetrievable::No>,
545 Retrievable<mozilla::Utf8Unit>, Retrievable<char16_t>,
546 Missing>;
549 // Start of fields.
552 mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refs = {};
554 // An id for this source that is unique across the process. This can be used
555 // to refer to this source from places that don't want to hold a strong
556 // reference on the source itself.
558 // This is a 32 bit ID and could overflow, in which case the ID will not be
559 // unique anymore.
560 uint32_t id_ = 0;
562 // Source data (as a mozilla::Variant).
563 SourceType data = SourceType(Missing());
565 // If the GC calls triggerConvertToCompressedSource with PinnedUnits present,
566 // the last PinnedUnits instance will install the compressed chars upon
567 // destruction.
569 // Retrievability isn't part of the type here because uncompressed->compressed
570 // transitions must preserve existing retrievability.
571 struct ReaderInstances {
572 size_t count = 0;
573 mozilla::MaybeOneOf<CompressedData<mozilla::Utf8Unit>,
574 CompressedData<char16_t>>
575 pendingCompressed;
577 ExclusiveData<ReaderInstances> readers_;
579 // The UTF-8 encoded filename of this script.
580 SharedImmutableString filename_;
582 // Hash of the script filename;
583 HashNumber filenameHash_ = 0;
585 // If this ScriptSource was generated by a code-introduction mechanism such
586 // as |eval| or |new Function|, the debugger needs access to the "raw"
587 // filename of the top-level script that contains the eval-ing code. To
588 // keep track of this, we must preserve the original outermost filename (of
589 // the original introducer script), so that instead of a filename of
590 // "foo.js line 30 > eval line 10 > Function", we can obtain the original
591 // raw filename of "foo.js".
593 // In the case described above, this field will be set to to the original raw
594 // UTF-8 encoded filename from above, otherwise it will be mozilla::Nothing.
595 SharedImmutableString introducerFilename_;
597 SharedImmutableTwoByteString displayURL_;
598 SharedImmutableTwoByteString sourceMapURL_;
600 // The bytecode cache encoder is used to encode only the content of function
601 // which are delazified. If this value is not nullptr, then each delazified
602 // function should be recorded before their first execution.
603 StencilIncrementalEncoderPtr xdrEncoder_;
605 // A string indicating how this source code was introduced into the system.
606 // This is a constant, statically allocated C string, so does not need memory
607 // management.
609 // TODO: Document the various additional introduction type constants.
610 const char* introductionType_ = nullptr;
612 // Bytecode offset in caller script that generated this code. This is
613 // present for eval-ed code, as well as "new Function(...)"-introduced
614 // scripts.
615 mozilla::Maybe<uint32_t> introductionOffset_;
617 // If this source is for Function constructor, the position of ")" after
618 // parameter list in the source. This is used to get function body.
619 // 0 for other cases.
620 uint32_t parameterListEnd_ = 0;
622 // Line number within the file where this source starts (1-origin).
623 uint32_t startLine_ = 0;
624 // Column number within the file where this source starts,
625 // in UTF-16 code units.
626 JS::LimitedColumnNumberOneOrigin startColumn_;
628 // See: CompileOptions::mutedErrors.
629 bool mutedErrors_ = false;
631 // Carry the delazification mode per source.
632 JS::DelazificationOption delazificationMode_ =
633 JS::DelazificationOption::OnDemandOnly;
635 // True if an associated SourceCompressionTask was ever created.
636 bool hadCompressionTask_ = false;
639 // End of fields.
642 // How many ids have been handed out to sources.
643 static mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent> idCount_;
645 template <typename Unit>
646 const Unit* chunkUnits(JSContext* cx,
647 UncompressedSourceCache::AutoHoldEntry& holder,
648 size_t chunk);
650 // Return a string containing the chars starting at |begin| and ending at
651 // |begin + len|.
653 // Warning: this is *not* GC-safe! Any chars to be handed out must use
654 // PinnedUnits. See comment below.
655 template <typename Unit>
656 const Unit* units(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp,
657 size_t begin, size_t len);
659 template <typename Unit>
660 const Unit* uncompressedUnits(size_t begin, size_t len);
662 public:
663 // When creating a JSString* from TwoByte source characters, we don't try to
664 // to deflate to Latin1 for longer strings, because this can be slow.
665 static const size_t SourceDeflateLimit = 100;
667 explicit ScriptSource()
668 : id_(++idCount_), readers_(js::mutexid::SourceCompression) {}
669 ~ScriptSource() { MOZ_ASSERT(refs == 0); }
671 void AddRef() { refs++; }
672 void Release() {
673 MOZ_ASSERT(refs != 0);
674 if (--refs == 0) {
675 js_delete(this);
678 [[nodiscard]] bool initFromOptions(FrontendContext* fc,
679 const JS::ReadOnlyCompileOptions& options);
682 * The minimum script length (in code units) necessary for a script to be
683 * eligible to be compressed.
685 static constexpr size_t MinimumCompressibleLength = 256;
687 SharedImmutableString getOrCreateStringZ(FrontendContext* fc,
688 UniqueChars&& str);
689 SharedImmutableTwoByteString getOrCreateStringZ(FrontendContext* fc,
690 UniqueTwoByteChars&& str);
692 private:
693 class LoadSourceMatcher;
695 public:
696 // Attempt to load usable source for |ss| -- source text on which substring
697 // operations and the like can be performed. On success return true and set
698 // |*loaded| to indicate whether usable source could be loaded; otherwise
699 // return false.
700 static bool loadSource(JSContext* cx, ScriptSource* ss, bool* loaded);
702 // Assign source data from |srcBuf| to this recently-created |ScriptSource|.
703 template <typename Unit>
704 [[nodiscard]] bool assignSource(FrontendContext* fc,
705 const JS::ReadOnlyCompileOptions& options,
706 JS::SourceText<Unit>& srcBuf);
708 bool hasSourceText() const {
709 return hasUncompressedSource() || hasCompressedSource();
712 private:
713 template <typename Unit>
714 struct UncompressedDataMatcher {
715 template <SourceRetrievable CanRetrieve>
716 const UncompressedData<Unit>* operator()(
717 const Uncompressed<Unit, CanRetrieve>& u) {
718 return &u;
721 template <typename T>
722 const UncompressedData<Unit>* operator()(const T&) {
723 MOZ_CRASH(
724 "attempting to access uncompressed data in a ScriptSource not "
725 "containing it");
726 return nullptr;
730 public:
731 template <typename Unit>
732 const UncompressedData<Unit>* uncompressedData() {
733 return data.match(UncompressedDataMatcher<Unit>());
736 private:
737 template <typename Unit>
738 struct CompressedDataMatcher {
739 template <SourceRetrievable CanRetrieve>
740 const CompressedData<Unit>* operator()(
741 const Compressed<Unit, CanRetrieve>& c) {
742 return &c;
745 template <typename T>
746 const CompressedData<Unit>* operator()(const T&) {
747 MOZ_CRASH(
748 "attempting to access compressed data in a ScriptSource not "
749 "containing it");
750 return nullptr;
754 public:
755 template <typename Unit>
756 const CompressedData<Unit>* compressedData() {
757 return data.match(CompressedDataMatcher<Unit>());
760 private:
761 struct HasUncompressedSource {
762 template <typename Unit, SourceRetrievable CanRetrieve>
763 bool operator()(const Uncompressed<Unit, CanRetrieve>&) {
764 return true;
767 template <typename Unit, SourceRetrievable CanRetrieve>
768 bool operator()(const Compressed<Unit, CanRetrieve>&) {
769 return false;
772 template <typename Unit>
773 bool operator()(const Retrievable<Unit>&) {
774 return false;
777 bool operator()(const Missing&) { return false; }
780 public:
781 bool hasUncompressedSource() const {
782 return data.match(HasUncompressedSource());
785 private:
786 template <typename Unit>
787 struct IsUncompressed {
788 template <SourceRetrievable CanRetrieve>
789 bool operator()(const Uncompressed<Unit, CanRetrieve>&) {
790 return true;
793 template <typename T>
794 bool operator()(const T&) {
795 return false;
799 public:
800 template <typename Unit>
801 bool isUncompressed() const {
802 return data.match(IsUncompressed<Unit>());
805 private:
806 struct HasCompressedSource {
807 template <typename Unit, SourceRetrievable CanRetrieve>
808 bool operator()(const Compressed<Unit, CanRetrieve>&) {
809 return true;
812 template <typename T>
813 bool operator()(const T&) {
814 return false;
818 public:
819 bool hasCompressedSource() const { return data.match(HasCompressedSource()); }
821 private:
822 template <typename Unit>
823 struct IsCompressed {
824 template <SourceRetrievable CanRetrieve>
825 bool operator()(const Compressed<Unit, CanRetrieve>&) {
826 return true;
829 template <typename T>
830 bool operator()(const T&) {
831 return false;
835 public:
836 template <typename Unit>
837 bool isCompressed() const {
838 return data.match(IsCompressed<Unit>());
841 private:
842 template <typename Unit>
843 struct SourceTypeMatcher {
844 template <template <typename C, SourceRetrievable R> class Data,
845 SourceRetrievable CanRetrieve>
846 bool operator()(const Data<Unit, CanRetrieve>&) {
847 return true;
850 template <template <typename C, SourceRetrievable R> class Data,
851 typename NotUnit, SourceRetrievable CanRetrieve>
852 bool operator()(const Data<NotUnit, CanRetrieve>&) {
853 return false;
856 bool operator()(const Retrievable<Unit>&) {
857 MOZ_CRASH("source type only applies where actual text is available");
858 return false;
861 template <typename NotUnit>
862 bool operator()(const Retrievable<NotUnit>&) {
863 return false;
866 bool operator()(const Missing&) {
867 MOZ_CRASH("doesn't make sense to ask source type when missing");
868 return false;
872 public:
873 template <typename Unit>
874 bool hasSourceType() const {
875 return data.match(SourceTypeMatcher<Unit>());
878 private:
879 struct UncompressedLengthMatcher {
880 template <typename Unit, SourceRetrievable CanRetrieve>
881 size_t operator()(const Uncompressed<Unit, CanRetrieve>& u) {
882 return u.length();
885 template <typename Unit, SourceRetrievable CanRetrieve>
886 size_t operator()(const Compressed<Unit, CanRetrieve>& u) {
887 return u.uncompressedLength;
890 template <typename Unit>
891 size_t operator()(const Retrievable<Unit>&) {
892 MOZ_CRASH("ScriptSource::length on a missing-but-retrievable source");
893 return 0;
896 size_t operator()(const Missing& m) {
897 MOZ_CRASH("ScriptSource::length on a missing source");
898 return 0;
902 public:
903 size_t length() const {
904 MOZ_ASSERT(hasSourceText());
905 return data.match(UncompressedLengthMatcher());
908 JSLinearString* substring(JSContext* cx, size_t start, size_t stop);
909 JSLinearString* substringDontDeflate(JSContext* cx, size_t start,
910 size_t stop);
912 [[nodiscard]] bool appendSubstring(JSContext* cx, js::StringBuilder& buf,
913 size_t start, size_t stop);
915 void setParameterListEnd(uint32_t parameterListEnd) {
916 parameterListEnd_ = parameterListEnd;
919 bool isFunctionBody() { return parameterListEnd_ != 0; }
920 JSLinearString* functionBodyString(JSContext* cx);
922 void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
923 JS::ScriptSourceInfo* info) const;
925 private:
926 // Overwrites |data| with the uncompressed data from |source|.
928 // This function asserts nothing about |data|. Users should use assertions to
929 // double-check their own understandings of the |data| state transition being
930 // performed.
931 template <typename ContextT, typename Unit>
932 [[nodiscard]] bool setUncompressedSourceHelper(ContextT* cx,
933 EntryUnits<Unit>&& source,
934 size_t length,
935 SourceRetrievable retrievable);
937 public:
938 // Initialize a fresh |ScriptSource| with unretrievable, uncompressed source.
939 template <typename Unit>
940 [[nodiscard]] bool initializeUnretrievableUncompressedSource(
941 FrontendContext* fc, EntryUnits<Unit>&& source, size_t length);
943 // Set the retrieved source for a |ScriptSource| whose source was recorded as
944 // missing but retrievable.
945 template <typename Unit>
946 [[nodiscard]] bool setRetrievedSource(JSContext* cx,
947 EntryUnits<Unit>&& source,
948 size_t length);
950 [[nodiscard]] bool tryCompressOffThread(JSContext* cx);
952 // Called by the SourceCompressionTask constructor to indicate such a task was
953 // ever created.
954 void noteSourceCompressionTask() { hadCompressionTask_ = true; }
956 // *Trigger* the conversion of this ScriptSource from containing uncompressed
957 // |Unit|-encoded source to containing compressed source. Conversion may not
958 // be complete when this function returns: it'll be delayed if there's ongoing
959 // use of the uncompressed source via |PinnedUnits|, in which case conversion
960 // won't occur until the outermost |PinnedUnits| is destroyed.
962 // Compressed source is in bytes, no matter that |Unit| might be |char16_t|.
963 // |sourceLength| is the length in code units (not bytes) of the uncompressed
964 // source.
965 template <typename Unit>
966 void triggerConvertToCompressedSource(SharedImmutableString compressed,
967 size_t sourceLength);
969 // Initialize a fresh ScriptSource as containing unretrievable compressed
970 // source of the indicated original encoding.
971 template <typename Unit>
972 [[nodiscard]] bool initializeWithUnretrievableCompressedSource(
973 FrontendContext* fc, UniqueChars&& raw, size_t rawLength,
974 size_t sourceLength);
976 private:
977 void performTaskWork(SourceCompressionTask* task);
979 struct TriggerConvertToCompressedSourceFromTask {
980 ScriptSource* const source_;
981 SharedImmutableString& compressed_;
983 TriggerConvertToCompressedSourceFromTask(ScriptSource* source,
984 SharedImmutableString& compressed)
985 : source_(source), compressed_(compressed) {}
987 template <typename Unit, SourceRetrievable CanRetrieve>
988 void operator()(const Uncompressed<Unit, CanRetrieve>&) {
989 source_->triggerConvertToCompressedSource<Unit>(std::move(compressed_),
990 source_->length());
993 template <typename Unit, SourceRetrievable CanRetrieve>
994 void operator()(const Compressed<Unit, CanRetrieve>&) {
995 MOZ_CRASH(
996 "can't set compressed source when source is already compressed -- "
997 "ScriptSource::tryCompressOffThread shouldn't have queued up this "
998 "task?");
1001 template <typename Unit>
1002 void operator()(const Retrievable<Unit>&) {
1003 MOZ_CRASH("shouldn't compressing unloaded-but-retrievable source");
1006 void operator()(const Missing&) {
1007 MOZ_CRASH(
1008 "doesn't make sense to set compressed source for missing source -- "
1009 "ScriptSource::tryCompressOffThread shouldn't have queued up this "
1010 "task?");
1014 template <typename Unit>
1015 void convertToCompressedSource(SharedImmutableString compressed,
1016 size_t uncompressedLength);
1018 template <typename Unit>
1019 void performDelayedConvertToCompressedSource(
1020 ExclusiveData<ReaderInstances>::Guard& g);
1022 void triggerConvertToCompressedSourceFromTask(
1023 SharedImmutableString compressed);
1025 public:
1026 HashNumber filenameHash() const { return filenameHash_; }
1027 const char* filename() const {
1028 return filename_ ? filename_.chars() : nullptr;
1030 [[nodiscard]] bool setFilename(FrontendContext* fc, const char* filename);
1031 [[nodiscard]] bool setFilename(FrontendContext* fc, UniqueChars&& filename);
1033 bool hasIntroducerFilename() const {
1034 return introducerFilename_ ? true : false;
1036 const char* introducerFilename() const {
1037 return introducerFilename_ ? introducerFilename_.chars() : filename();
1039 [[nodiscard]] bool setIntroducerFilename(FrontendContext* fc,
1040 const char* filename);
1041 [[nodiscard]] bool setIntroducerFilename(FrontendContext* fc,
1042 UniqueChars&& filename);
1044 bool hasIntroductionType() const { return introductionType_; }
1045 const char* introductionType() const {
1046 MOZ_ASSERT(hasIntroductionType());
1047 return introductionType_;
1050 uint32_t id() const { return id_; }
1052 // Display URLs
1053 [[nodiscard]] bool setDisplayURL(FrontendContext* fc, const char16_t* url);
1054 [[nodiscard]] bool setDisplayURL(FrontendContext* fc,
1055 UniqueTwoByteChars&& url);
1056 bool hasDisplayURL() const { return bool(displayURL_); }
1057 const char16_t* displayURL() { return displayURL_.chars(); }
1059 // Source maps
1060 [[nodiscard]] bool setSourceMapURL(FrontendContext* fc, const char16_t* url);
1061 [[nodiscard]] bool setSourceMapURL(FrontendContext* fc,
1062 UniqueTwoByteChars&& url);
1063 bool hasSourceMapURL() const { return bool(sourceMapURL_); }
1064 const char16_t* sourceMapURL() { return sourceMapURL_.chars(); }
1066 bool mutedErrors() const { return mutedErrors_; }
1068 uint32_t startLine() const { return startLine_; }
1069 JS::LimitedColumnNumberOneOrigin startColumn() const { return startColumn_; }
1071 JS::DelazificationOption delazificationMode() const {
1072 return delazificationMode_;
1075 bool hasIntroductionOffset() const { return introductionOffset_.isSome(); }
1076 uint32_t introductionOffset() const { return introductionOffset_.value(); }
1077 void setIntroductionOffset(uint32_t offset) {
1078 MOZ_ASSERT(!hasIntroductionOffset());
1079 MOZ_ASSERT(offset <= (uint32_t)INT32_MAX);
1080 introductionOffset_.emplace(offset);
1083 // Return wether an XDR encoder is present or not.
1084 bool hasEncoder() const { return xdrEncoder_.hasEncoder(); }
1086 [[nodiscard]] bool startIncrementalEncoding(
1087 JSContext* cx,
1088 UniquePtr<frontend::ExtensibleCompilationStencil>&& initial,
1089 bool& alreadyStarted);
1091 [[nodiscard]] bool addDelazificationToIncrementalEncoding(
1092 JSContext* cx, const frontend::CompilationStencil& stencil);
1094 // Linearize the encoded content in the |buffer| provided as argument to
1095 // |xdrEncodeTopLevel|, and free the XDR encoder. In case of errors, the
1096 // |buffer| is considered undefined.
1097 bool xdrFinalizeEncoder(JSContext* cx, JS::TranscodeBuffer& buffer);
1099 bool xdrFinalizeEncoder(JSContext* cx, JS::Stencil** stencilOut);
1101 // Discard the incremental encoding data and free the XDR encoder.
1102 void xdrAbortEncoder();
1105 // [SMDOC] ScriptSourceObject
1107 // ScriptSourceObject stores the ScriptSource and GC pointers related to it.
1108 class ScriptSourceObject : public NativeObject {
1109 static const JSClassOps classOps_;
1111 public:
1112 static const JSClass class_;
1114 static void finalize(JS::GCContext* gcx, JSObject* obj);
1116 static ScriptSourceObject* create(JSContext* cx, ScriptSource* source);
1118 // Initialize those properties of this ScriptSourceObject whose values
1119 // are provided by |options|, re-wrapping as necessary.
1120 static bool initFromOptions(JSContext* cx,
1121 JS::Handle<ScriptSourceObject*> source,
1122 const JS::InstantiateOptions& options);
1124 static bool initElementProperties(JSContext* cx,
1125 JS::Handle<ScriptSourceObject*> source,
1126 HandleString elementAttrName);
1128 bool hasSource() const { return !getReservedSlot(SOURCE_SLOT).isUndefined(); }
1129 ScriptSource* source() const {
1130 return static_cast<ScriptSource*>(getReservedSlot(SOURCE_SLOT).toPrivate());
1133 JSObject* unwrappedElement(JSContext* cx) const;
1135 const Value& unwrappedElementAttributeName() const {
1136 MOZ_ASSERT(isInitialized());
1137 const Value& v = getReservedSlot(ELEMENT_PROPERTY_SLOT);
1138 MOZ_ASSERT(!v.isMagic());
1139 return v;
1141 BaseScript* unwrappedIntroductionScript() const {
1142 MOZ_ASSERT(isInitialized());
1143 Value value = getReservedSlot(INTRODUCTION_SCRIPT_SLOT);
1144 if (value.isUndefined()) {
1145 return nullptr;
1147 return value.toGCThing()->as<BaseScript>();
1150 void setPrivate(JSRuntime* rt, const Value& value);
1151 void clearPrivate(JSRuntime* rt);
1153 void setIntroductionScript(const Value& introductionScript) {
1154 setReservedSlot(INTRODUCTION_SCRIPT_SLOT, introductionScript);
1157 Value getPrivate() const {
1158 MOZ_ASSERT(isInitialized());
1159 Value value = getReservedSlot(PRIVATE_SLOT);
1160 return value;
1163 private:
1164 #ifdef DEBUG
1165 bool isInitialized() const {
1166 Value element = getReservedSlot(ELEMENT_PROPERTY_SLOT);
1167 if (element.isMagic(JS_GENERIC_MAGIC)) {
1168 return false;
1170 return !getReservedSlot(INTRODUCTION_SCRIPT_SLOT).isMagic(JS_GENERIC_MAGIC);
1172 #endif
1174 enum {
1175 SOURCE_SLOT = 0,
1176 ELEMENT_PROPERTY_SLOT,
1177 INTRODUCTION_SCRIPT_SLOT,
1178 PRIVATE_SLOT,
1179 RESERVED_SLOTS
1183 // ScriptWarmUpData represents a pointer-sized field in BaseScript that stores
1184 // one of the following using low-bit tags:
1186 // * The enclosing BaseScript. This is only used while this script is lazy and
1187 // its containing script is also lazy. This outer script must be compiled
1188 // before the current script can in order to correctly build the scope chain.
1190 // * The enclosing Scope. This is only used while this script is lazy and its
1191 // containing script is compiled. This is the outer scope chain that will be
1192 // used to compile this scipt.
1194 // * The script's warm-up count. This is only used until the script has a
1195 // JitScript. The Baseline Interpreter and JITs use the warm-up count stored
1196 // in JitScript.
1198 // * A pointer to the JitScript, when the script is warm enough for the Baseline
1199 // Interpreter.
1201 class ScriptWarmUpData {
1202 uintptr_t data_ = ResetState();
1204 private:
1205 static constexpr uintptr_t NumTagBits = 2;
1206 static constexpr uint32_t MaxWarmUpCount = UINT32_MAX >> NumTagBits;
1208 public:
1209 // Public only for the JITs.
1210 static constexpr uintptr_t TagMask = (1 << NumTagBits) - 1;
1211 static constexpr uintptr_t JitScriptTag = 0;
1212 static constexpr uintptr_t EnclosingScriptTag = 1;
1213 static constexpr uintptr_t EnclosingScopeTag = 2;
1214 static constexpr uintptr_t WarmUpCountTag = 3;
1216 private:
1217 // A gc-safe value to clear to.
1218 constexpr uintptr_t ResetState() { return 0 | WarmUpCountTag; }
1220 template <uintptr_t Tag>
1221 inline void setTaggedPtr(void* ptr) {
1222 static_assert(Tag <= TagMask, "Tag must fit in TagMask");
1223 MOZ_ASSERT((uintptr_t(ptr) & TagMask) == 0);
1224 data_ = uintptr_t(ptr) | Tag;
1227 template <typename T, uintptr_t Tag>
1228 inline T getTaggedPtr() const {
1229 static_assert(Tag <= TagMask, "Tag must fit in TagMask");
1230 MOZ_ASSERT((data_ & TagMask) == Tag);
1231 return reinterpret_cast<T>(data_ & ~TagMask);
1234 void setWarmUpCount(uint32_t count) {
1235 if (count > MaxWarmUpCount) {
1236 count = MaxWarmUpCount;
1238 data_ = (uintptr_t(count) << NumTagBits) | WarmUpCountTag;
1241 public:
1242 void trace(JSTracer* trc);
1244 bool isEnclosingScript() const {
1245 return (data_ & TagMask) == EnclosingScriptTag;
1247 bool isEnclosingScope() const {
1248 return (data_ & TagMask) == EnclosingScopeTag;
1250 bool isWarmUpCount() const { return (data_ & TagMask) == WarmUpCountTag; }
1251 bool isJitScript() const { return (data_ & TagMask) == JitScriptTag; }
1253 // NOTE: To change type safely, 'clear' the old tagged value and then 'init'
1254 // the new one. This will notify the GC appropriately.
1256 BaseScript* toEnclosingScript() const {
1257 return getTaggedPtr<BaseScript*, EnclosingScriptTag>();
1259 inline void initEnclosingScript(BaseScript* enclosingScript);
1260 inline void clearEnclosingScript();
1262 Scope* toEnclosingScope() const {
1263 return getTaggedPtr<Scope*, EnclosingScopeTag>();
1265 inline void initEnclosingScope(Scope* enclosingScope);
1266 inline void clearEnclosingScope();
1268 uint32_t toWarmUpCount() const {
1269 MOZ_ASSERT(isWarmUpCount());
1270 return data_ >> NumTagBits;
1272 void resetWarmUpCount(uint32_t count) {
1273 MOZ_ASSERT(isWarmUpCount());
1274 setWarmUpCount(count);
1276 void incWarmUpCount() {
1277 MOZ_ASSERT(isWarmUpCount());
1278 data_ += uintptr_t(1) << NumTagBits;
1281 jit::JitScript* toJitScript() const {
1282 return getTaggedPtr<jit::JitScript*, JitScriptTag>();
1284 void initJitScript(jit::JitScript* jitScript) {
1285 MOZ_ASSERT(isWarmUpCount());
1286 setTaggedPtr<JitScriptTag>(jitScript);
1288 void clearJitScript() {
1289 MOZ_ASSERT(isJitScript());
1290 data_ = ResetState();
1292 } JS_HAZ_GC_POINTER;
1294 static_assert(sizeof(ScriptWarmUpData) == sizeof(uintptr_t),
1295 "JIT code depends on ScriptWarmUpData being pointer-sized");
1297 // [SMDOC] - JSScript data layout (unshared)
1299 // PrivateScriptData stores variable-length data associated with a script.
1300 // Abstractly a PrivateScriptData consists of the following:
1302 // * A non-empty array of GCCellPtr in gcthings()
1304 // Accessing this array just requires calling the appropriate public
1305 // Span-computing function.
1307 // This class doesn't use the GC barrier wrapper classes. BaseScript::swapData
1308 // performs a manual pre-write barrier when detaching PrivateScriptData from a
1309 // script.
1310 class alignas(uintptr_t) PrivateScriptData final
1311 : public TrailingArray<PrivateScriptData> {
1312 private:
1313 uint32_t ngcthings = 0;
1315 // Note: This is only defined for scripts with an enclosing scope. This
1316 // excludes lazy scripts with lazy parents.
1317 js::MemberInitializers memberInitializers_ =
1318 js::MemberInitializers::Invalid();
1320 // End of fields.
1322 private:
1323 // Layout helpers
1324 Offset gcThingsOffset() { return offsetOfGCThings(); }
1325 Offset endOffset() const {
1326 uintptr_t size = ngcthings * sizeof(JS::GCCellPtr);
1327 return offsetOfGCThings() + size;
1330 // Initialize header and PackedSpans
1331 explicit PrivateScriptData(uint32_t ngcthings);
1333 public:
1334 static constexpr size_t offsetOfGCThings() {
1335 return sizeof(PrivateScriptData);
1338 // Accessors for typed array spans.
1339 mozilla::Span<JS::GCCellPtr> gcthings() {
1340 Offset offset = offsetOfGCThings();
1341 return mozilla::Span{offsetToPointer<JS::GCCellPtr>(offset), ngcthings};
1344 void setMemberInitializers(MemberInitializers memberInitializers) {
1345 MOZ_ASSERT(memberInitializers_.valid == false,
1346 "Only init MemberInitializers once");
1347 memberInitializers_ = memberInitializers;
1349 const MemberInitializers& getMemberInitializers() {
1350 return memberInitializers_;
1353 // Allocate a new PrivateScriptData. Headers and GCCellPtrs are initialized.
1354 static PrivateScriptData* new_(JSContext* cx, uint32_t ngcthings);
1356 static bool InitFromStencil(
1357 JSContext* cx, js::HandleScript script,
1358 const js::frontend::CompilationAtomCache& atomCache,
1359 const js::frontend::CompilationStencil& stencil,
1360 js::frontend::CompilationGCOutput& gcOutput,
1361 const js::frontend::ScriptIndex scriptIndex);
1363 void trace(JSTracer* trc);
1365 size_t allocationSize() const;
1367 // PrivateScriptData has trailing data so isn't copyable or movable.
1368 PrivateScriptData(const PrivateScriptData&) = delete;
1369 PrivateScriptData& operator=(const PrivateScriptData&) = delete;
1372 // [SMDOC] Script Representation (js::BaseScript)
1374 // A "script" corresponds to a JavaScript function or a top-level (global, eval,
1375 // module) body that will be executed using SpiderMonkey bytecode. Note that
1376 // special forms such as asm.js do not use bytecode or the BaseScript type.
1378 // BaseScript may be generated directly from the parser/emitter, or by cloning
1379 // or deserializing another script. Cloning is typically used when a script is
1380 // needed in multiple realms and we would like to avoid re-compiling.
1382 // A single script may be shared by multiple JSFunctions in a realm when those
1383 // function objects are used as closure. In this case, a single JSFunction is
1384 // considered canonical (and often does not escape to script directly).
1386 // A BaseScript may be in "lazy" form where the parser performs a syntax-only
1387 // parse and saves minimal information. These lazy scripts must be recompiled
1388 // from the source (generating bytecode) before they can execute in a process
1389 // called "delazification". On GC memory pressure, a fully-compiled script may
1390 // be converted back into lazy form by "relazification".
1392 // A fully-initialized BaseScript can be identified with `hasBytecode()` and
1393 // will have bytecode and set of GC-things such as scopes, inner-functions, and
1394 // object/string literals. This is referred to as a "non-lazy" script.
1396 // A lazy script has either an enclosing script or scope. Each script needs to
1397 // know its enclosing scope in order to be fully compiled. If the parent is
1398 // still lazy we track that script and will need to compile it first to know our
1399 // own enclosing scope. This is because scope objects are not created until full
1400 // compilation and bytecode generation.
1403 // # Script Warm-Up #
1405 // A script evolves its representation over time. As it becomes "hotter" we
1406 // attach a stack of additional data-structures generated by the JITs to
1407 // speed-up execution. This evolution may also be run in reverse, in order to
1408 // reduce memory usage.
1410 // +-------------------------------------+
1411 // | ScriptSource |
1412 // | Provides: Source |
1413 // | Engine: Parser |
1414 // +-------------------------------------+
1415 // v
1416 // +-----------------------------------------------+
1417 // | BaseScript |
1418 // | Provides: SourceExtent/Bindings |
1419 // | Engine: CompileLazyFunctionToStencil |
1420 // | /InstantiateStencilsForDelazify |
1421 // +-----------------------------------------------+
1422 // v
1423 // +-------------------------------------+
1424 // | ImmutableScriptData |
1425 // | Provides: Bytecode |
1426 // | Engine: Interpreter |
1427 // +-------------------------------------+
1428 // v
1429 // +-------------------------------------+
1430 // | JitScript |
1431 // | Provides: Inline Caches (ICs) |
1432 // | Engine: BaselineInterpreter |
1433 // +-------------------------------------+
1434 // v
1435 // +-------------------------------------+
1436 // | BaselineScript |
1437 // | Provides: Native Code |
1438 // | Engine: Baseline |
1439 // +-------------------------------------+
1440 // v
1441 // +-------------------------------------+
1442 // | IonScript |
1443 // | Provides: Optimized Native Code |
1444 // | Engine: IonMonkey |
1445 // +-------------------------------------+
1447 // NOTE: Scripts may be directly created with bytecode and skip the lazy script
1448 // form. This is always the case for top-level scripts.
1449 class BaseScript : public gc::TenuredCellWithNonGCPointer<uint8_t> {
1450 friend class js::gc::CellAllocator;
1452 public:
1453 // Pointer to baseline->method()->raw(), ion->method()->raw(), a wasm jit
1454 // entry, the JIT's EnterInterpreter stub, or the lazy link stub. Must be
1455 // non-null (except on no-jit builds). This is stored in the cell header.
1456 uint8_t* jitCodeRaw() const { return headerPtr(); }
1458 protected:
1459 // Multi-purpose value that changes type as the script warms up from lazy form
1460 // to interpreted-bytecode to JITs. See: ScriptWarmUpData type for more info.
1461 ScriptWarmUpData warmUpData_ = {};
1463 // For function scripts this is the canonical function, otherwise nullptr.
1464 const GCPtr<JSFunction*> function_ = {};
1466 // The ScriptSourceObject for this script. This is always same-compartment and
1467 // same-realm with this script.
1468 const GCPtr<ScriptSourceObject*> sourceObject_ = {};
1470 // Position of the function in the source buffer. Both in terms of line/column
1471 // and code-unit offset.
1472 const SourceExtent extent_ = {};
1474 // Immutable flags are a combination of parser options and bytecode
1475 // characteristics. These flags are preserved when serializing or copying this
1476 // script.
1477 const ImmutableScriptFlags immutableFlags_ = {};
1479 // Mutable flags store transient information used by subsystems such as the
1480 // debugger and the JITs. These flags are *not* preserved when serializing or
1481 // cloning since they are based on runtime state.
1482 MutableScriptFlags mutableFlags_ = {};
1484 // Variable-length data owned by this script. This stores one of:
1485 // - GC pointers that bytecode references.
1486 // - Inner-functions and bindings generated by syntax parse.
1487 // - Nullptr, if no bytecode or inner functions.
1488 // This is updated as script is delazified and relazified.
1489 GCStructPtr<PrivateScriptData*> data_;
1491 // Shareable script data. This includes runtime-wide atom pointers, bytecode,
1492 // and various script note structures. If the script is currently lazy, this
1493 // will be nullptr.
1494 RefPtr<js::SharedImmutableScriptData> sharedData_ = {};
1496 // End of fields.
1498 BaseScript(uint8_t* stubEntry, JSFunction* function,
1499 ScriptSourceObject* sourceObject, const SourceExtent& extent,
1500 uint32_t immutableFlags);
1502 void setJitCodeRaw(uint8_t* code) { setHeaderPtr(code); }
1504 public:
1505 static BaseScript* New(JSContext* cx, JS::Handle<JSFunction*> function,
1506 JS::Handle<js::ScriptSourceObject*> sourceObject,
1507 const js::SourceExtent& extent,
1508 uint32_t immutableFlags);
1510 // Create a lazy BaseScript without initializing any gc-things.
1511 static BaseScript* CreateRawLazy(JSContext* cx, uint32_t ngcthings,
1512 HandleFunction fun,
1513 JS::Handle<ScriptSourceObject*> sourceObject,
1514 const SourceExtent& extent,
1515 uint32_t immutableFlags);
1517 bool isUsingInterpreterTrampoline(JSRuntime* rt) const;
1519 // Canonical function for the script, if it has a function. For top-level
1520 // scripts this is nullptr.
1521 JSFunction* function() const { return function_; }
1523 JS::Realm* realm() const { return sourceObject()->realm(); }
1524 JS::Compartment* compartment() const { return sourceObject()->compartment(); }
1525 JS::Compartment* maybeCompartment() const { return compartment(); }
1526 inline JSPrincipals* principals() const;
1528 ScriptSourceObject* sourceObject() const { return sourceObject_; }
1529 ScriptSource* scriptSource() const { return sourceObject()->source(); }
1530 ScriptSource* maybeForwardedScriptSource() const;
1532 bool mutedErrors() const { return scriptSource()->mutedErrors(); }
1534 const char* filename() const { return scriptSource()->filename(); }
1535 HashNumber filenameHash() const { return scriptSource()->filenameHash(); }
1536 const char* maybeForwardedFilename() const {
1537 return maybeForwardedScriptSource()->filename();
1540 uint32_t sourceStart() const { return extent_.sourceStart; }
1541 uint32_t sourceEnd() const { return extent_.sourceEnd; }
1542 uint32_t sourceLength() const {
1543 return extent_.sourceEnd - extent_.sourceStart;
1545 uint32_t toStringStart() const { return extent_.toStringStart; }
1546 uint32_t toStringEnd() const { return extent_.toStringEnd; }
1547 SourceExtent extent() const { return extent_; }
1549 [[nodiscard]] bool appendSourceDataForToString(JSContext* cx,
1550 js::StringBuilder& buf);
1552 // Line number (1-origin)
1553 uint32_t lineno() const { return extent_.lineno; }
1554 // Column number in UTF-16 code units
1555 JS::LimitedColumnNumberOneOrigin column() const { return extent_.column; }
1557 JS::DelazificationOption delazificationMode() const {
1558 return scriptSource()->delazificationMode();
1561 public:
1562 ImmutableScriptFlags immutableFlags() const { return immutableFlags_; }
1563 RO_IMMUTABLE_SCRIPT_FLAGS(immutableFlags_)
1564 RW_MUTABLE_SCRIPT_FLAGS(mutableFlags_)
1566 bool hasEnclosingScript() const { return warmUpData_.isEnclosingScript(); }
1567 BaseScript* enclosingScript() const {
1568 return warmUpData_.toEnclosingScript();
1570 void setEnclosingScript(BaseScript* enclosingScript);
1572 // Returns true is the script has an enclosing scope but no bytecode. It is
1573 // ready for delazification.
1574 // NOTE: The enclosing script must have been successfully compiled at some
1575 // point for the enclosing scope to exist. That script may have since been
1576 // GC'd, but we kept the scope live so we can still compile ourselves.
1577 bool isReadyForDelazification() const {
1578 return warmUpData_.isEnclosingScope();
1581 Scope* enclosingScope() const;
1582 void setEnclosingScope(Scope* enclosingScope);
1583 Scope* releaseEnclosingScope();
1585 bool hasJitScript() const { return warmUpData_.isJitScript(); }
1586 jit::JitScript* jitScript() const {
1587 MOZ_ASSERT(hasJitScript());
1588 return warmUpData_.toJitScript();
1590 jit::JitScript* maybeJitScript() const {
1591 return hasJitScript() ? jitScript() : nullptr;
1594 inline bool hasBaselineScript() const;
1595 inline bool hasIonScript() const;
1597 bool hasPrivateScriptData() const { return data_ != nullptr; }
1599 // Update data_ pointer while also informing GC MemoryUse tracking.
1600 void swapData(UniquePtr<PrivateScriptData>& other);
1602 mozilla::Span<const JS::GCCellPtr> gcthings() const {
1603 return data_ ? data_->gcthings() : mozilla::Span<JS::GCCellPtr>();
1606 // NOTE: This is only used to initialize a fresh script.
1607 mozilla::Span<JS::GCCellPtr> gcthingsForInit() {
1608 MOZ_ASSERT(!hasBytecode());
1609 return data_ ? data_->gcthings() : mozilla::Span<JS::GCCellPtr>();
1612 void setMemberInitializers(MemberInitializers memberInitializers) {
1613 MOZ_ASSERT(useMemberInitializers());
1614 MOZ_ASSERT(data_);
1615 data_->setMemberInitializers(memberInitializers);
1617 const MemberInitializers& getMemberInitializers() const {
1618 MOZ_ASSERT(data_);
1619 return data_->getMemberInitializers();
1622 SharedImmutableScriptData* sharedData() const { return sharedData_; }
1623 void initSharedData(SharedImmutableScriptData* data) {
1624 MOZ_ASSERT(sharedData_ == nullptr);
1625 sharedData_ = data;
1627 void freeSharedData() { sharedData_ = nullptr; }
1629 // NOTE: Script only has bytecode if JSScript::fullyInitFromStencil completes
1630 // successfully.
1631 bool hasBytecode() const {
1632 if (sharedData_) {
1633 MOZ_ASSERT(data_);
1634 MOZ_ASSERT(warmUpData_.isWarmUpCount() || warmUpData_.isJitScript());
1635 return true;
1637 return false;
1640 public:
1641 static const JS::TraceKind TraceKind = JS::TraceKind::Script;
1643 void traceChildren(JSTracer* trc);
1644 void finalize(JS::GCContext* gcx);
1646 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
1647 return mallocSizeOf(data_);
1650 inline JSScript* asJSScript();
1652 // JIT accessors
1653 static constexpr size_t offsetOfJitCodeRaw() { return offsetOfHeaderPtr(); }
1654 static constexpr size_t offsetOfPrivateData() {
1655 return offsetof(BaseScript, data_);
1657 static constexpr size_t offsetOfSharedData() {
1658 return offsetof(BaseScript, sharedData_);
1660 static size_t offsetOfImmutableFlags() {
1661 static_assert(sizeof(ImmutableScriptFlags) == sizeof(uint32_t));
1662 return offsetof(BaseScript, immutableFlags_);
1664 static constexpr size_t offsetOfMutableFlags() {
1665 static_assert(sizeof(MutableScriptFlags) == sizeof(uint32_t));
1666 return offsetof(BaseScript, mutableFlags_);
1668 static constexpr size_t offsetOfWarmUpData() {
1669 return offsetof(BaseScript, warmUpData_);
1672 #if defined(DEBUG) || defined(JS_JITSPEW)
1673 void dumpStringContent(js::GenericPrinter& out) const;
1674 #endif
1677 extern void SweepScriptData(JSRuntime* rt);
1679 } /* namespace js */
1681 class JSScript : public js::BaseScript {
1682 private:
1683 friend bool js::PrivateScriptData::InitFromStencil(
1684 JSContext* cx, js::HandleScript script,
1685 const js::frontend::CompilationAtomCache& atomCache,
1686 const js::frontend::CompilationStencil& stencil,
1687 js::frontend::CompilationGCOutput& gcOutput,
1688 const js::frontend::ScriptIndex scriptIndex);
1690 private:
1691 using js::BaseScript::BaseScript;
1693 public:
1694 static JSScript* Create(JSContext* cx, JS::Handle<JSFunction*> function,
1695 JS::Handle<js::ScriptSourceObject*> sourceObject,
1696 const js::SourceExtent& extent,
1697 js::ImmutableScriptFlags flags);
1699 // NOTE: This should only be used while delazifying.
1700 static JSScript* CastFromLazy(js::BaseScript* lazy) {
1701 return static_cast<JSScript*>(lazy);
1704 // NOTE: If you use createPrivateScriptData directly instead of via
1705 // fullyInitFromStencil, you are responsible for notifying the debugger
1706 // after successfully creating the script.
1707 static bool createPrivateScriptData(JSContext* cx,
1708 JS::Handle<JSScript*> script,
1709 uint32_t ngcthings);
1711 public:
1712 static bool fullyInitFromStencil(
1713 JSContext* cx, const js::frontend::CompilationAtomCache& atomCache,
1714 const js::frontend::CompilationStencil& stencil,
1715 js::frontend::CompilationGCOutput& gcOutput, js::HandleScript script,
1716 const js::frontend::ScriptIndex scriptIndex);
1718 // Allocate a JSScript and initialize it with bytecode. This consumes
1719 // allocations within the stencil.
1720 static JSScript* fromStencil(JSContext* cx,
1721 js::frontend::CompilationAtomCache& atomCache,
1722 const js::frontend::CompilationStencil& stencil,
1723 js::frontend::CompilationGCOutput& gcOutput,
1724 js::frontend::ScriptIndex scriptIndex);
1726 #ifdef DEBUG
1727 private:
1728 // Assert that jump targets are within the code array of the script.
1729 void assertValidJumpTargets() const;
1730 #endif
1732 public:
1733 js::ImmutableScriptData* immutableScriptData() const {
1734 return sharedData_->get();
1737 // Script bytecode is immutable after creation.
1738 jsbytecode* code() const {
1739 if (!sharedData_) {
1740 return nullptr;
1742 return immutableScriptData()->code();
1745 bool hasForceInterpreterOp() const {
1746 // JSOp::ForceInterpreter, if present, must be the first op.
1747 MOZ_ASSERT(length() >= 1);
1748 return JSOp(*code()) == JSOp::ForceInterpreter;
1751 js::AllBytecodesIterable allLocations() {
1752 return js::AllBytecodesIterable(this);
1755 js::BytecodeLocation location() { return js::BytecodeLocation(this, code()); }
1757 size_t length() const {
1758 MOZ_ASSERT(sharedData_);
1759 return immutableScriptData()->codeLength();
1762 jsbytecode* codeEnd() const { return code() + length(); }
1764 jsbytecode* lastPC() const {
1765 jsbytecode* pc = codeEnd() - js::JSOpLength_RetRval;
1766 MOZ_ASSERT(JSOp(*pc) == JSOp::RetRval || JSOp(*pc) == JSOp::Return);
1767 return pc;
1770 // Note: ArgBytes is optional, but if specified then containsPC will also
1771 // check that the opcode arguments are in bounds.
1772 template <size_t ArgBytes = 0>
1773 bool containsPC(const jsbytecode* pc) const {
1774 MOZ_ASSERT_IF(ArgBytes,
1775 js::GetBytecodeLength(pc) == sizeof(jsbytecode) + ArgBytes);
1776 const jsbytecode* lastByte = pc + ArgBytes;
1777 return pc >= code() && lastByte < codeEnd();
1779 template <typename ArgType>
1780 bool containsPC(const jsbytecode* pc) const {
1781 return containsPC<sizeof(ArgType)>(pc);
1784 bool contains(const js::BytecodeLocation& loc) const {
1785 return containsPC(loc.toRawBytecode());
1788 size_t pcToOffset(const jsbytecode* pc) const {
1789 MOZ_ASSERT(containsPC(pc));
1790 return size_t(pc - code());
1793 jsbytecode* offsetToPC(size_t offset) const {
1794 MOZ_ASSERT(offset < length());
1795 return code() + offset;
1798 size_t mainOffset() const { return immutableScriptData()->mainOffset; }
1800 // The fixed part of a stack frame is comprised of vars (in function and
1801 // module code) and block-scoped locals (in all kinds of code).
1802 size_t nfixed() const { return immutableScriptData()->nfixed; }
1804 // Number of fixed slots reserved for slots that are always live. Only
1805 // nonzero for function or module code.
1806 size_t numAlwaysLiveFixedSlots() const;
1808 // Calculate the number of fixed slots that are live at a particular bytecode.
1809 size_t calculateLiveFixed(jsbytecode* pc);
1811 size_t nslots() const { return immutableScriptData()->nslots; }
1813 unsigned numArgs() const;
1815 inline js::Shape* initialEnvironmentShape() const;
1817 bool functionHasParameterExprs() const;
1819 bool functionAllowsParameterRedeclaration() const {
1820 // Parameter redeclaration is only allowed for non-strict functions with
1821 // simple parameter lists, which are neither arrow nor method functions. We
1822 // don't have a flag at hand to test the function kind, but we can still
1823 // test if the function is non-strict and has a simple parameter list by
1824 // checking |hasMappedArgsObj()|. (Mapped arguments objects are only
1825 // created for non-strict functions with simple parameter lists.)
1826 return hasMappedArgsObj();
1829 size_t numICEntries() const { return immutableScriptData()->numICEntries; }
1831 size_t funLength() const { return immutableScriptData()->funLength; }
1833 void cacheForEval() {
1834 MOZ_ASSERT(isForEval());
1835 // IsEvalCacheCandidate will make sure that there's nothing in this
1836 // script that would prevent reexecution even if isRunOnce is
1837 // true. So just pretend like we never ran this script.
1838 clearFlag(MutableFlags::HasRunOnce);
1842 * Arguments access (via JSOp::*Arg* opcodes) must access the canonical
1843 * location for the argument. If an arguments object exists AND it's mapped
1844 * ('arguments' aliases formals), then all access must go through the
1845 * arguments object. Otherwise, the local slot is the canonical location for
1846 * the arguments. Note: if a formal is aliased through the scope chain, then
1847 * script->formalIsAliased and JSOp::*Arg* opcodes won't be emitted at all.
1849 bool argsObjAliasesFormals() const {
1850 return needsArgsObj() && hasMappedArgsObj();
1853 void updateJitCodeRaw(JSRuntime* rt);
1855 bool isModule() const;
1856 js::ModuleObject* module() const;
1858 bool isGlobalCode() const;
1860 // Returns true if the script may read formal arguments on the stack
1861 // directly, via lazy arguments or a rest parameter.
1862 bool mayReadFrameArgsDirectly();
1864 static JSLinearString* sourceData(JSContext* cx, JS::HandleScript script);
1866 #ifdef MOZ_VTUNE
1867 // Unique Method ID passed to the VTune profiler. Allows attribution of
1868 // different jitcode to the same source script.
1869 uint32_t vtuneMethodID();
1870 #endif
1872 public:
1873 /* Return whether this is a 'direct eval' script in a function scope. */
1874 bool isDirectEvalInFunction() const;
1877 * Return whether this script is a top-level script.
1879 * If we evaluate some code which contains a syntax error, then we might
1880 * produce a JSScript which has no associated bytecode. Testing with
1881 * |code()| filters out this kind of scripts.
1883 * If this script has a function associated to it, then it is not the
1884 * top-level of a file.
1886 bool isTopLevel() { return code() && !isFunction(); }
1888 /* Ensure the script has a JitScript. */
1889 inline bool ensureHasJitScript(JSContext* cx, js::jit::AutoKeepJitScripts&);
1891 void maybeReleaseJitScript(JS::GCContext* gcx);
1892 void releaseJitScript(JS::GCContext* gcx);
1893 void releaseJitScriptOnFinalize(JS::GCContext* gcx);
1895 inline js::jit::BaselineScript* baselineScript() const;
1896 inline js::jit::IonScript* ionScript() const;
1898 inline bool isIonCompilingOffThread() const;
1899 inline bool canIonCompile() const;
1900 inline void disableIon();
1902 inline bool canBaselineCompile() const;
1903 inline void disableBaselineCompile();
1905 inline js::GlobalObject& global() const;
1906 inline bool hasGlobal(const js::GlobalObject* global) const;
1907 js::GlobalObject& uninlinedGlobal() const;
1909 js::GCThingIndex bodyScopeIndex() const {
1910 return immutableScriptData()->bodyScopeIndex;
1913 js::Scope* bodyScope() const { return getScope(bodyScopeIndex()); }
1915 js::Scope* outermostScope() const {
1916 // The body scope may not be the outermost scope in the script when
1917 // the decl env scope is present.
1918 return getScope(js::GCThingIndex::outermostScopeIndex());
1921 bool functionHasExtraBodyVarScope() const {
1922 bool res = BaseScript::functionHasExtraBodyVarScope();
1923 MOZ_ASSERT_IF(res, functionHasParameterExprs());
1924 return res;
1927 js::VarScope* functionExtraBodyVarScope() const;
1929 bool needsBodyEnvironment() const;
1931 inline js::LexicalScope* maybeNamedLambdaScope() const;
1933 // Drop script data and reset warmUpData to reference enclosing scope.
1934 void relazify(JSRuntime* rt);
1936 private:
1937 bool createJitScript(JSContext* cx);
1939 bool shareScriptData(JSContext* cx);
1941 public:
1942 inline uint32_t getWarmUpCount() const;
1943 inline void incWarmUpCounter();
1944 inline void resetWarmUpCounterForGC();
1946 inline void updateLastICStubCounter();
1947 inline uint32_t warmUpCountAtLastICStub() const;
1949 void resetWarmUpCounterToDelayIonCompilation();
1951 unsigned getWarmUpResetCount() const {
1952 constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK);
1953 return mutableFlags_ & MASK;
1955 void incWarmUpResetCounter() {
1956 constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK);
1957 uint32_t newCount = getWarmUpResetCount() + 1;
1958 if (newCount <= MASK) {
1959 mutableFlags_ &= ~MASK;
1960 mutableFlags_ |= newCount;
1963 void resetWarmUpResetCounter() {
1964 constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK);
1965 mutableFlags_ &= ~MASK;
1968 public:
1969 bool initScriptCounts(JSContext* cx);
1970 js::ScriptCounts& getScriptCounts();
1971 js::PCCounts* maybeGetPCCounts(jsbytecode* pc);
1972 const js::PCCounts* maybeGetThrowCounts(jsbytecode* pc);
1973 js::PCCounts* getThrowCounts(jsbytecode* pc);
1974 uint64_t getHitCount(jsbytecode* pc);
1975 void addIonCounts(js::jit::IonScriptCounts* ionCounts);
1976 js::jit::IonScriptCounts* getIonCounts();
1977 void releaseScriptCounts(js::ScriptCounts* counts);
1978 void destroyScriptCounts();
1979 void resetScriptCounts();
1981 jsbytecode* main() const { return code() + mainOffset(); }
1983 js::BytecodeLocation mainLocation() const {
1984 return js::BytecodeLocation(this, main());
1987 js::BytecodeLocation endLocation() const {
1988 return js::BytecodeLocation(this, codeEnd());
1991 js::BytecodeLocation offsetToLocation(uint32_t offset) const {
1992 return js::BytecodeLocation(this, offsetToPC(offset));
1995 void addSizeOfJitScript(mozilla::MallocSizeOf mallocSizeOf,
1996 size_t* sizeOfJitScript,
1997 size_t* sizeOfAllocSites) const;
1999 mozilla::Span<const js::TryNote> trynotes() const {
2000 return immutableScriptData()->tryNotes();
2003 mozilla::Span<const js::ScopeNote> scopeNotes() const {
2004 return immutableScriptData()->scopeNotes();
2007 mozilla::Span<const uint32_t> resumeOffsets() const {
2008 return immutableScriptData()->resumeOffsets();
2011 uint32_t tableSwitchCaseOffset(jsbytecode* pc, uint32_t caseIndex) const {
2012 MOZ_ASSERT(containsPC(pc));
2013 MOZ_ASSERT(JSOp(*pc) == JSOp::TableSwitch);
2014 uint32_t firstResumeIndex = GET_RESUMEINDEX(pc + 3 * JUMP_OFFSET_LEN);
2015 return resumeOffsets()[firstResumeIndex + caseIndex];
2017 jsbytecode* tableSwitchCasePC(jsbytecode* pc, uint32_t caseIndex) const {
2018 return offsetToPC(tableSwitchCaseOffset(pc, caseIndex));
2021 bool hasLoops();
2023 uint32_t numNotes() const {
2024 MOZ_ASSERT(sharedData_);
2025 return immutableScriptData()->noteLength();
2027 js::SrcNote* notes() const {
2028 MOZ_ASSERT(sharedData_);
2029 return immutableScriptData()->notes();
2031 js::SrcNote* notesEnd() const {
2032 MOZ_ASSERT(sharedData_);
2033 return immutableScriptData()->notes() + numNotes();
2036 JSString* getString(js::GCThingIndex index) const {
2037 return &gcthings()[index].as<JSString>();
2040 JSString* getString(jsbytecode* pc) const {
2041 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
2042 MOZ_ASSERT(js::JOF_OPTYPE((JSOp)*pc) == JOF_STRING);
2043 return getString(GET_GCTHING_INDEX(pc));
2046 bool atomizeString(JSContext* cx, jsbytecode* pc) {
2047 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
2048 MOZ_ASSERT(js::JOF_OPTYPE((JSOp)*pc) == JOF_STRING);
2049 js::GCThingIndex index = GET_GCTHING_INDEX(pc);
2050 JSString* str = getString(index);
2051 if (str->isAtom()) {
2052 return true;
2054 JSAtom* atom = js::AtomizeString(cx, str);
2055 if (!atom) {
2056 return false;
2058 js::gc::CellPtrPreWriteBarrier(data_->gcthings()[index]);
2059 data_->gcthings()[index] = JS::GCCellPtr(atom);
2060 return true;
2063 JSAtom* getAtom(js::GCThingIndex index) const {
2064 return &gcthings()[index].as<JSString>().asAtom();
2067 JSAtom* getAtom(jsbytecode* pc) const {
2068 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
2069 MOZ_ASSERT(js::JOF_OPTYPE((JSOp)*pc) == JOF_ATOM);
2070 return getAtom(GET_GCTHING_INDEX(pc));
2073 js::PropertyName* getName(js::GCThingIndex index) {
2074 return getAtom(index)->asPropertyName();
2077 js::PropertyName* getName(jsbytecode* pc) const {
2078 return getAtom(pc)->asPropertyName();
2081 JSObject* getObject(js::GCThingIndex index) const {
2082 MOZ_ASSERT(gcthings()[index].asCell()->isTenured());
2083 return &gcthings()[index].as<JSObject>();
2086 JSObject* getObject(const jsbytecode* pc) const {
2087 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
2088 return getObject(GET_GCTHING_INDEX(pc));
2091 js::SharedShape* getShape(js::GCThingIndex index) const {
2092 return &gcthings()[index].as<js::Shape>().asShared();
2095 js::SharedShape* getShape(const jsbytecode* pc) const {
2096 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
2097 return getShape(GET_GCTHING_INDEX(pc));
2100 js::Scope* getScope(js::GCThingIndex index) const {
2101 return &gcthings()[index].as<js::Scope>();
2104 js::Scope* getScope(jsbytecode* pc) const {
2105 // This method is used to get a scope directly using a JSOp with an
2106 // index. To search through ScopeNotes to look for a Scope using pc,
2107 // use lookupScope.
2108 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
2109 MOZ_ASSERT(js::JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPE,
2110 "Did you mean to use lookupScope(pc)?");
2111 return getScope(GET_GCTHING_INDEX(pc));
2114 inline JSFunction* getFunction(js::GCThingIndex index) const;
2115 inline JSFunction* getFunction(jsbytecode* pc) const;
2117 inline js::RegExpObject* getRegExp(js::GCThingIndex index) const;
2118 inline js::RegExpObject* getRegExp(jsbytecode* pc) const;
2120 js::BigInt* getBigInt(js::GCThingIndex index) const {
2121 MOZ_ASSERT(gcthings()[index].asCell()->isTenured());
2122 return &gcthings()[index].as<js::BigInt>();
2125 js::BigInt* getBigInt(jsbytecode* pc) const {
2126 MOZ_ASSERT(containsPC<js::GCThingIndex>(pc));
2127 MOZ_ASSERT(js::JOF_OPTYPE(JSOp(*pc)) == JOF_BIGINT);
2128 return getBigInt(GET_GCTHING_INDEX(pc));
2131 // The following 3 functions find the static scope just before the
2132 // execution of the instruction pointed to by pc.
2134 js::Scope* lookupScope(const jsbytecode* pc) const;
2136 js::Scope* innermostScope(const jsbytecode* pc) const;
2137 js::Scope* innermostScope() const { return innermostScope(main()); }
2140 * The isEmpty method tells whether this script has code that computes any
2141 * result (not return value, result AKA normal completion value) other than
2142 * JSVAL_VOID, or any other effects.
2144 bool isEmpty() const {
2145 if (length() > 3) {
2146 return false;
2149 jsbytecode* pc = code();
2150 if (noScriptRval() && JSOp(*pc) == JSOp::False) {
2151 ++pc;
2153 return JSOp(*pc) == JSOp::RetRval;
2156 bool formalIsAliased(unsigned argSlot);
2157 bool anyFormalIsForwarded();
2158 bool formalLivesInArgumentsObject(unsigned argSlot);
2160 // See comment above 'debugMode' in Realm.h for explanation of
2161 // invariants of debuggee compartments, scripts, and frames.
2162 inline bool isDebuggee() const;
2164 // A helper class to prevent relazification of the given function's script
2165 // while it's holding on to it. This class automatically roots the script.
2166 class AutoDelazify;
2167 friend class AutoDelazify;
2169 class AutoDelazify {
2170 JS::RootedScript script_;
2171 JSContext* cx_;
2172 bool oldAllowRelazify_ = false;
2174 public:
2175 explicit AutoDelazify(JSContext* cx, JS::HandleFunction fun = nullptr)
2176 : script_(cx), cx_(cx) {
2177 holdScript(fun);
2180 ~AutoDelazify() { dropScript(); }
2182 void operator=(JS::HandleFunction fun) {
2183 dropScript();
2184 holdScript(fun);
2187 operator JS::HandleScript() const { return script_; }
2188 explicit operator bool() const { return script_; }
2190 private:
2191 void holdScript(JS::HandleFunction fun);
2192 void dropScript();
2195 #if defined(DEBUG) || defined(JS_JITSPEW)
2196 public:
2197 struct DumpOptions {
2198 bool recursive = false;
2199 bool runtimeData = false;
2202 void dump(JSContext* cx);
2203 void dumpRecursive(JSContext* cx);
2205 static bool dump(JSContext* cx, JS::Handle<JSScript*> script,
2206 DumpOptions& options, js::StringPrinter* sp);
2207 static bool dumpSrcNotes(JSContext* cx, JS::Handle<JSScript*> script,
2208 js::GenericPrinter* sp);
2209 static bool dumpTryNotes(JSContext* cx, JS::Handle<JSScript*> script,
2210 js::GenericPrinter* sp);
2211 static bool dumpScopeNotes(JSContext* cx, JS::Handle<JSScript*> script,
2212 js::GenericPrinter* sp);
2213 static bool dumpGCThings(JSContext* cx, JS::Handle<JSScript*> script,
2214 js::GenericPrinter* sp);
2215 #endif
2218 namespace js {
2220 struct ScriptAndCounts {
2221 /* This structure is stored and marked from the JSRuntime. */
2222 JSScript* script;
2223 ScriptCounts scriptCounts;
2225 inline explicit ScriptAndCounts(JSScript* script);
2226 inline ScriptAndCounts(ScriptAndCounts&& sac);
2228 const PCCounts* maybeGetPCCounts(jsbytecode* pc) const {
2229 return scriptCounts.maybeGetPCCounts(script->pcToOffset(pc));
2231 const PCCounts* maybeGetThrowCounts(jsbytecode* pc) const {
2232 return scriptCounts.maybeGetThrowCounts(script->pcToOffset(pc));
2235 jit::IonScriptCounts* getIonCounts() const { return scriptCounts.ionCounts_; }
2237 void trace(JSTracer* trc) {
2238 TraceRoot(trc, &script, "ScriptAndCounts::script");
2242 extern JS::UniqueChars FormatIntroducedFilename(const char* filename,
2243 uint32_t lineno,
2244 const char* introducer);
2246 extern jsbytecode* LineNumberToPC(JSScript* script, unsigned lineno);
2248 extern JS_PUBLIC_API unsigned GetScriptLineExtent(
2249 JSScript* script, JS::LimitedColumnNumberOneOrigin* columnp = nullptr);
2251 #ifdef JS_CACHEIR_SPEW
2252 void maybeUpdateWarmUpCount(JSScript* script);
2253 void maybeSpewScriptFinalWarmUpCount(JSScript* script);
2254 #endif
2256 } /* namespace js */
2258 namespace js {
2260 extern unsigned PCToLineNumber(
2261 JSScript* script, jsbytecode* pc,
2262 JS::LimitedColumnNumberOneOrigin* columnp = nullptr);
2264 extern unsigned PCToLineNumber(
2265 unsigned startLine, JS::LimitedColumnNumberOneOrigin startCol,
2266 SrcNote* notes, SrcNote* notesEnd, jsbytecode* code, jsbytecode* pc,
2267 JS::LimitedColumnNumberOneOrigin* columnp = nullptr);
2270 * This function returns the file and line number of the script currently
2271 * executing on cx. If there is no current script executing on cx (e.g., a
2272 * native called directly through JSAPI (e.g., by setTimeout)), nullptr and 0
2273 * are returned as the file and line.
2275 extern void DescribeScriptedCallerForCompilation(
2276 JSContext* cx, MutableHandleScript maybeScript, const char** file,
2277 uint32_t* linenop, uint32_t* pcOffset, bool* mutedErrors);
2280 * Like DescribeScriptedCallerForCompilation, but this function avoids looking
2281 * up the script/pc and the full linear scan to compute line number.
2283 extern void DescribeScriptedCallerForDirectEval(
2284 JSContext* cx, HandleScript script, jsbytecode* pc, const char** file,
2285 uint32_t* linenop, uint32_t* pcOffset, bool* mutedErrors);
2287 bool CheckCompileOptionsMatch(const JS::ReadOnlyCompileOptions& options,
2288 js::ImmutableScriptFlags flags);
2290 void FillImmutableFlagsFromCompileOptionsForTopLevel(
2291 const JS::ReadOnlyCompileOptions& options, js::ImmutableScriptFlags& flags);
2293 void FillImmutableFlagsFromCompileOptionsForFunction(
2294 const JS::ReadOnlyCompileOptions& options, js::ImmutableScriptFlags& flags);
2296 } /* namespace js */
2298 namespace JS {
2300 template <>
2301 struct GCPolicy<js::ScriptLCovEntry>
2302 : public IgnoreGCPolicy<js::ScriptLCovEntry> {};
2304 #ifdef JS_CACHEIR_SPEW
2305 template <>
2306 struct GCPolicy<js::ScriptFinalWarmUpCountEntry>
2307 : public IgnoreGCPolicy<js::ScriptFinalWarmUpCountEntry> {};
2308 #endif
2310 namespace ubi {
2312 template <>
2313 class Concrete<JSScript> : public Concrete<js::BaseScript> {};
2315 } // namespace ubi
2316 } // namespace JS
2318 #endif /* vm_JSScript_h */