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. */
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
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
47 struct ScriptSourceInfo
;
48 template <typename UnitT
>
54 class FrontendContext
;
60 class JS_PUBLIC_API Sprinter
;
64 } // namespace coverage
71 class AutoKeepJitScripts
;
74 struct IonScriptCounts
;
80 class SourceCompressionTask
;
86 struct CompilationStencil
;
87 struct ExtensibleCompilationStencil
;
88 struct CompilationGCOutput
;
89 struct CompilationStencilMerger
;
91 } // namespace frontend
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
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; }
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
>;
183 using ScriptVTuneIdMap
=
184 GCRekeyableHashMap
<HeapPtr
<BaseScript
*>, uint32_t,
185 DefaultHasher
<HeapPtr
<BaseScript
*>>, SystemAllocPolicy
>;
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
>;
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
{
201 frontend::CompilationStencilMerger
* merger_
= nullptr;
203 StencilIncrementalEncoderPtr() = default;
204 ~StencilIncrementalEncoderPtr() { reset(); }
206 bool hasEncoder() const { return bool(merger_
); }
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;
221 ScriptSourceChunk() = default;
223 ScriptSourceChunk(ScriptSource
* ss
, uint32_t chunk
) : ss(ss
), chunk(chunk
) {
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
),
241 static bool match(const ScriptSourceChunk
& c1
, const ScriptSourceChunk
& 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
,
270 // Hold an entry in the source data cache and prevent it from being purged on
272 class AutoHoldEntry
{
273 UncompressedSourceCache
* cache_
= nullptr;
274 ScriptSourceChunk sourceChunk_
= {};
275 SourceData data_
= nullptr;
278 explicit AutoHoldEntry() = default;
282 MOZ_ASSERT(sourceChunk_
.valid());
283 cache_
->releaseEntry(*this);
287 template <typename Unit
>
288 void holdUnits(EntryUnits
<Unit
> units
) {
290 MOZ_ASSERT(!sourceChunk_
.valid());
293 data_
= ToSourceData(std::move(units
));
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.
303 MOZ_ASSERT(!sourceChunk_
.valid());
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.
314 MOZ_ASSERT(sourceChunk_
.valid());
318 sourceChunk_
= ScriptSourceChunk();
320 data_
= std::move(data
);
323 const ScriptSourceChunk
& sourceChunk() const { return sourceChunk_
; }
324 friend class UncompressedSourceCache
;
328 UniquePtr
<Map
> map_
= nullptr;
329 AutoHoldEntry
* holder_
= nullptr;
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
);
341 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
);
344 void holdEntry(AutoHoldEntry
& holder
, const ScriptSourceChunk
& ssc
);
345 void releaseEntry(AutoHoldEntry
& holder
);
348 template <typename Unit
>
349 struct SourceTypeTraits
;
352 struct SourceTypeTraits
<mozilla::Utf8Unit
> {
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
) {
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
);
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.
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
;
419 // Common base class of the templated variants of PinnedUnits<T>.
420 class PinnedUnitsBase
{
422 ScriptSource
* source_
;
424 explicit PinnedUnitsBase(ScriptSource
* source
) : source_(source
) {}
428 template <typename Unit
>
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
{
443 PinnedUnits(JSContext
* cx
, ScriptSource
* source
,
444 UncompressedSourceCache::AutoHoldEntry
& holder
, size_t begin
,
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
{
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());
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.)
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
>
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_
;
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
>;
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
>;
535 // The set of currently allowed encoding modes.
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
>,
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
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
569 // Retrievability isn't part of the type here because uncompressed->compressed
570 // transitions must preserve existing retrievability.
571 struct ReaderInstances
{
573 mozilla::MaybeOneOf
<CompressedData
<mozilla::Utf8Unit
>,
574 CompressedData
<char16_t
>>
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
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
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;
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
,
650 // Return a string containing the chars starting at |begin| and ending at
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
);
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
++; }
673 MOZ_ASSERT(refs
!= 0);
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
,
689 SharedImmutableTwoByteString
getOrCreateStringZ(FrontendContext
* fc
,
690 UniqueTwoByteChars
&& str
);
693 class LoadSourceMatcher
;
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
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();
713 template <typename Unit
>
714 struct UncompressedDataMatcher
{
715 template <SourceRetrievable CanRetrieve
>
716 const UncompressedData
<Unit
>* operator()(
717 const Uncompressed
<Unit
, CanRetrieve
>& u
) {
721 template <typename T
>
722 const UncompressedData
<Unit
>* operator()(const T
&) {
724 "attempting to access uncompressed data in a ScriptSource not "
731 template <typename Unit
>
732 const UncompressedData
<Unit
>* uncompressedData() {
733 return data
.match(UncompressedDataMatcher
<Unit
>());
737 template <typename Unit
>
738 struct CompressedDataMatcher
{
739 template <SourceRetrievable CanRetrieve
>
740 const CompressedData
<Unit
>* operator()(
741 const Compressed
<Unit
, CanRetrieve
>& c
) {
745 template <typename T
>
746 const CompressedData
<Unit
>* operator()(const T
&) {
748 "attempting to access compressed data in a ScriptSource not "
755 template <typename Unit
>
756 const CompressedData
<Unit
>* compressedData() {
757 return data
.match(CompressedDataMatcher
<Unit
>());
761 struct HasUncompressedSource
{
762 template <typename Unit
, SourceRetrievable CanRetrieve
>
763 bool operator()(const Uncompressed
<Unit
, CanRetrieve
>&) {
767 template <typename Unit
, SourceRetrievable CanRetrieve
>
768 bool operator()(const Compressed
<Unit
, CanRetrieve
>&) {
772 template <typename Unit
>
773 bool operator()(const Retrievable
<Unit
>&) {
777 bool operator()(const Missing
&) { return false; }
781 bool hasUncompressedSource() const {
782 return data
.match(HasUncompressedSource());
786 template <typename Unit
>
787 struct IsUncompressed
{
788 template <SourceRetrievable CanRetrieve
>
789 bool operator()(const Uncompressed
<Unit
, CanRetrieve
>&) {
793 template <typename T
>
794 bool operator()(const T
&) {
800 template <typename Unit
>
801 bool isUncompressed() const {
802 return data
.match(IsUncompressed
<Unit
>());
806 struct HasCompressedSource
{
807 template <typename Unit
, SourceRetrievable CanRetrieve
>
808 bool operator()(const Compressed
<Unit
, CanRetrieve
>&) {
812 template <typename T
>
813 bool operator()(const T
&) {
819 bool hasCompressedSource() const { return data
.match(HasCompressedSource()); }
822 template <typename Unit
>
823 struct IsCompressed
{
824 template <SourceRetrievable CanRetrieve
>
825 bool operator()(const Compressed
<Unit
, CanRetrieve
>&) {
829 template <typename T
>
830 bool operator()(const T
&) {
836 template <typename Unit
>
837 bool isCompressed() const {
838 return data
.match(IsCompressed
<Unit
>());
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
>&) {
850 template <template <typename C
, SourceRetrievable R
> class Data
,
851 typename NotUnit
, SourceRetrievable CanRetrieve
>
852 bool operator()(const Data
<NotUnit
, CanRetrieve
>&) {
856 bool operator()(const Retrievable
<Unit
>&) {
857 MOZ_CRASH("source type only applies where actual text is available");
861 template <typename NotUnit
>
862 bool operator()(const Retrievable
<NotUnit
>&) {
866 bool operator()(const Missing
&) {
867 MOZ_CRASH("doesn't make sense to ask source type when missing");
873 template <typename Unit
>
874 bool hasSourceType() const {
875 return data
.match(SourceTypeMatcher
<Unit
>());
879 struct UncompressedLengthMatcher
{
880 template <typename Unit
, SourceRetrievable CanRetrieve
>
881 size_t operator()(const Uncompressed
<Unit
, CanRetrieve
>& u
) {
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");
896 size_t operator()(const Missing
& m
) {
897 MOZ_CRASH("ScriptSource::length on a missing source");
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
,
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;
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
931 template <typename ContextT
, typename Unit
>
932 [[nodiscard
]] bool setUncompressedSourceHelper(ContextT
* cx
,
933 EntryUnits
<Unit
>&& source
,
935 SourceRetrievable retrievable
);
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
,
950 [[nodiscard
]] bool tryCompressOffThread(JSContext
* cx
);
952 // Called by the SourceCompressionTask constructor to indicate such a task was
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
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
);
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_
),
993 template <typename Unit
, SourceRetrievable CanRetrieve
>
994 void operator()(const Compressed
<Unit
, CanRetrieve
>&) {
996 "can't set compressed source when source is already compressed -- "
997 "ScriptSource::tryCompressOffThread shouldn't have queued up this "
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
&) {
1008 "doesn't make sense to set compressed source for missing source -- "
1009 "ScriptSource::tryCompressOffThread shouldn't have queued up this "
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
);
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_
; }
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(); }
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(
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_
;
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());
1141 BaseScript
* unwrappedIntroductionScript() const {
1142 MOZ_ASSERT(isInitialized());
1143 Value value
= getReservedSlot(INTRODUCTION_SCRIPT_SLOT
);
1144 if (value
.isUndefined()) {
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
);
1165 bool isInitialized() const {
1166 Value element
= getReservedSlot(ELEMENT_PROPERTY_SLOT
);
1167 if (element
.isMagic(JS_GENERIC_MAGIC
)) {
1170 return !getReservedSlot(INTRODUCTION_SCRIPT_SLOT
).isMagic(JS_GENERIC_MAGIC
);
1176 ELEMENT_PROPERTY_SLOT
,
1177 INTRODUCTION_SCRIPT_SLOT
,
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
1198 // * A pointer to the JitScript, when the script is warm enough for the Baseline
1201 class ScriptWarmUpData
{
1202 uintptr_t data_
= ResetState();
1205 static constexpr uintptr_t NumTagBits
= 2;
1206 static constexpr uint32_t MaxWarmUpCount
= UINT32_MAX
>> NumTagBits
;
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;
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
;
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
1310 class alignas(uintptr_t) PrivateScriptData final
1311 : public TrailingArray
<PrivateScriptData
> {
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();
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
);
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 // +-------------------------------------+
1412 // | Provides: Source |
1413 // | Engine: Parser |
1414 // +-------------------------------------+
1416 // +-----------------------------------------------+
1418 // | Provides: SourceExtent/Bindings |
1419 // | Engine: CompileLazyFunctionToStencil |
1420 // | /InstantiateStencilsForDelazify |
1421 // +-----------------------------------------------+
1423 // +-------------------------------------+
1424 // | ImmutableScriptData |
1425 // | Provides: Bytecode |
1426 // | Engine: Interpreter |
1427 // +-------------------------------------+
1429 // +-------------------------------------+
1431 // | Provides: Inline Caches (ICs) |
1432 // | Engine: BaselineInterpreter |
1433 // +-------------------------------------+
1435 // +-------------------------------------+
1436 // | BaselineScript |
1437 // | Provides: Native Code |
1438 // | Engine: Baseline |
1439 // +-------------------------------------+
1441 // +-------------------------------------+
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
;
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(); }
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
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
1494 RefPtr
<js::SharedImmutableScriptData
> sharedData_
= {};
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
); }
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
,
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();
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());
1615 data_
->setMemberInitializers(memberInitializers
);
1617 const MemberInitializers
& getMemberInitializers() const {
1619 return data_
->getMemberInitializers();
1622 SharedImmutableScriptData
* sharedData() const { return sharedData_
; }
1623 void initSharedData(SharedImmutableScriptData
* data
) {
1624 MOZ_ASSERT(sharedData_
== nullptr);
1627 void freeSharedData() { sharedData_
= nullptr; }
1629 // NOTE: Script only has bytecode if JSScript::fullyInitFromStencil completes
1631 bool hasBytecode() const {
1634 MOZ_ASSERT(warmUpData_
.isWarmUpCount() || warmUpData_
.isJitScript());
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();
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;
1677 extern void SweepScriptData(JSRuntime
* rt
);
1679 } /* namespace js */
1681 class JSScript
: public js::BaseScript
{
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
);
1691 using js::BaseScript::BaseScript
;
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
);
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
);
1728 // Assert that jump targets are within the code array of the script.
1729 void assertValidJumpTargets() const;
1733 js::ImmutableScriptData
* immutableScriptData() const {
1734 return sharedData_
->get();
1737 // Script bytecode is immutable after creation.
1738 jsbytecode
* code() const {
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
);
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
);
1867 // Unique Method ID passed to the VTune profiler. Allows attribution of
1868 // different jitcode to the same source script.
1869 uint32_t vtuneMethodID();
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());
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
);
1937 bool createJitScript(JSContext
* cx
);
1939 bool shareScriptData(JSContext
* cx
);
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
;
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
));
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()) {
2054 JSAtom
* atom
= js::AtomizeString(cx
, str
);
2058 js::gc::CellPtrPreWriteBarrier(data_
->gcthings()[index
]);
2059 data_
->gcthings()[index
] = JS::GCCellPtr(atom
);
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,
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 {
2149 jsbytecode
* pc
= code();
2150 if (noScriptRval() && JSOp(*pc
) == JSOp::False
) {
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.
2167 friend class AutoDelazify
;
2169 class AutoDelazify
{
2170 JS::RootedScript script_
;
2172 bool oldAllowRelazify_
= false;
2175 explicit AutoDelazify(JSContext
* cx
, JS::HandleFunction fun
= nullptr)
2176 : script_(cx
), cx_(cx
) {
2180 ~AutoDelazify() { dropScript(); }
2182 void operator=(JS::HandleFunction fun
) {
2187 operator JS::HandleScript() const { return script_
; }
2188 explicit operator bool() const { return script_
; }
2191 void holdScript(JS::HandleFunction fun
);
2195 #if defined(DEBUG) || defined(JS_JITSPEW)
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
);
2220 struct ScriptAndCounts
{
2221 /* This structure is stored and marked from the JSRuntime. */
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
,
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
);
2256 } /* 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 */
2301 struct GCPolicy
<js::ScriptLCovEntry
>
2302 : public IgnoreGCPolicy
<js::ScriptLCovEntry
> {};
2304 #ifdef JS_CACHEIR_SPEW
2306 struct GCPolicy
<js::ScriptFinalWarmUpCountEntry
>
2307 : public IgnoreGCPolicy
<js::ScriptFinalWarmUpCountEntry
> {};
2313 class Concrete
<JSScript
> : public Concrete
<js::BaseScript
> {};
2318 #endif /* vm_JSScript_h */