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