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 #ifndef vm_SharedStencil_h
8 #define vm_SharedStencil_h
9 
10 #include "mozilla/Assertions.h"     // MOZ_ASSERT, MOZ_CRASH
11 #include "mozilla/Atomics.h"        // mozilla::{Atomic, SequentiallyConsistent}
12 #include "mozilla/CheckedInt.h"     // mozilla::CheckedInt
13 #include "mozilla/HashFunctions.h"  // mozilla::HahNumber, mozilla::HashBytes
14 #include "mozilla/HashTable.h"      // mozilla::HashSet
15 #include "mozilla/MemoryReporting.h"  // mozilla::MallocSizeOf
16 #include "mozilla/RefPtr.h"           // RefPtr
17 #include "mozilla/Span.h"             // mozilla::Span
18 
19 #include <stddef.h>  // size_t
20 #include <stdint.h>  // uint8_t, uint16_t, uint32_t
21 
22 #include "frontend/SourceNotes.h"  // js::SrcNote
23 #include "frontend/TypedIndex.h"   // js::frontend::TypedIndex
24 
25 #include "js/AllocPolicy.h"            // js::SystemAllocPolicy
26 #include "js/TypeDecls.h"              // JSContext,jsbytecode
27 #include "js/UniquePtr.h"              // js::UniquePtr
28 #include "util/EnumFlags.h"            // js::EnumFlags
29 #include "util/TrailingArray.h"        // js::TrailingArray
30 #include "vm/GeneratorAndAsyncKind.h"  // GeneratorKind, FunctionAsyncKind
31 #include "vm/StencilEnums.h"  // js::{TryNoteKind,ImmutableScriptFlagsEnum,MutableScriptFlagsEnum}
32 
33 //
34 // Data structures shared between Stencil and the VM.
35 //
36 
37 namespace js {
38 
39 namespace frontend {
40 class StencilXDR;
41 }  // namespace frontend
42 
43 // Index into gcthings array.
44 class GCThingIndexType;
45 class GCThingIndex : public frontend::TypedIndex<GCThingIndexType> {
46   // Delegate constructors;
47   using Base = frontend::TypedIndex<GCThingIndexType>;
48   using Base::Base;
49 
50  public:
outermostScopeIndex()51   static constexpr GCThingIndex outermostScopeIndex() {
52     return GCThingIndex(0);
53   }
54 
invalid()55   static constexpr GCThingIndex invalid() { return GCThingIndex(UINT32_MAX); }
56 
next()57   GCThingIndex next() const { return GCThingIndex(index + 1); }
58 };
59 
60 /*
61  * Exception handling record.
62  */
63 struct TryNote {
64   uint32_t kind_;      /* one of TryNoteKind */
65   uint32_t stackDepth; /* stack depth upon exception handler entry */
66   uint32_t start;      /* start of the try statement or loop relative
67                           to script->code() */
68   uint32_t length;     /* length of the try statement or loop */
69 
TryNoteTryNote70   TryNote(uint32_t kind, uint32_t stackDepth, uint32_t start, uint32_t length)
71       : kind_(kind), stackDepth(stackDepth), start(start), length(length) {}
72 
73   TryNote() = default;
74 
kindTryNote75   TryNoteKind kind() const { return TryNoteKind(kind_); }
76 
isLoopTryNote77   bool isLoop() const {
78     switch (kind()) {
79       case TryNoteKind::Loop:
80       case TryNoteKind::ForIn:
81       case TryNoteKind::ForOf:
82         return true;
83       case TryNoteKind::Catch:
84       case TryNoteKind::Finally:
85       case TryNoteKind::ForOfIterClose:
86       case TryNoteKind::Destructuring:
87         return false;
88     }
89     MOZ_CRASH("Unexpected try note kind");
90   }
91 };
92 
93 // A block scope has a range in bytecode: it is entered at some offset, and left
94 // at some later offset.  Scopes can be nested.  Given an offset, the
95 // ScopeNote containing that offset whose with the highest start value
96 // indicates the block scope.  The block scope list is sorted by increasing
97 // start value.
98 //
99 // It is possible to leave a scope nonlocally, for example via a "break"
100 // statement, so there may be short bytecode ranges in a block scope in which we
101 // are popping the block chain in preparation for a goto.  These exits are also
102 // nested with respect to outer scopes.  The scopes in these exits are indicated
103 // by the "index" field, just like any other block.  If a nonlocal exit pops the
104 // last block scope, the index will be NoScopeIndex.
105 //
106 struct ScopeNote {
107   // Sentinel index for no Scope.
108   static constexpr GCThingIndex NoScopeIndex = GCThingIndex::invalid();
109 
110   // Sentinel index for no ScopeNote.
111   static const uint32_t NoScopeNoteIndex = UINT32_MAX;
112 
113   // Index of the js::Scope in the script's gcthings array, or NoScopeIndex if
114   // there is no block scope in this range.
115   GCThingIndex index;
116 
117   // Bytecode offset at which this scope starts relative to script->code().
118   uint32_t start = 0;
119 
120   // Length of bytecode span this scope covers.
121   uint32_t length = 0;
122 
123   // Index of parent block scope in notes, or NoScopeNoteIndex.
124   uint32_t parent = 0;
125 };
126 
127 // Range of characters in scriptSource which contains a script's source,
128 // that is, the range used by the Parser to produce a script.
129 //
130 // For most functions the fields point to the following locations.
131 //
132 //   function * foo(a, b) { return a + b; }
133 //   ^             ^                       ^
134 //   |             |                       |
135 //   |             sourceStart     sourceEnd
136 //   |                                     |
137 //   toStringStart               toStringEnd
138 //
139 // For the special case of class constructors, the spec requires us to use an
140 // alternate definition of toStringStart / toStringEnd.
141 //
142 //   class C { constructor() { this.field = 42; } }
143 //   ^                    ^                      ^ ^
144 //   |                    |                      | |
145 //   |                    sourceStart    sourceEnd |
146 //   |                                             |
147 //   toStringStart                       toStringEnd
148 //
149 // Implicit class constructors use the following definitions.
150 //
151 //   class C { someMethod() { } }
152 //   ^                           ^
153 //   |                           |
154 //   sourceStart         sourceEnd
155 //   |                           |
156 //   toStringStart     toStringEnd
157 //
158 // Field initializer lambdas are internal details of the engine, but we still
159 // provide a sensible definition of these values.
160 //
161 //   class C { static field = 1 }
162 //   class C {        field = 1 }
163 //   class C {        somefield }
164 //                    ^        ^
165 //                    |        |
166 //          sourceStart        sourceEnd
167 //
168 // The non-static private class methods (including getters and setters) ALSO
169 // create a hidden initializer lambda in addition to the method itself. These
170 // lambdas are not exposed directly to script.
171 //
172 //   class C { #field() {       } }
173 //   class C { get #field() {   } }
174 //   class C { async #field() { } }
175 //   class C { * #field() {     } }
176 //             ^                 ^
177 //             |                 |
178 //             sourceStart       sourceEnd
179 //
180 // NOTE: These are counted in Code Units from the start of the script source.
181 //
182 // Also included in the SourceExtent is the line and column numbers of the
183 // sourceStart position. Compilation options may specify the initial line and
184 // column number.
185 //
186 // NOTE: Column number may saturate and must not be used as unique identifier.
187 struct SourceExtent {
188   SourceExtent() = default;
189 
SourceExtentSourceExtent190   SourceExtent(uint32_t sourceStart, uint32_t sourceEnd, uint32_t toStringStart,
191                uint32_t toStringEnd, uint32_t lineno, uint32_t column)
192       : sourceStart(sourceStart),
193         sourceEnd(sourceEnd),
194         toStringStart(toStringStart),
195         toStringEnd(toStringEnd),
196         lineno(lineno),
197         column(column) {}
198 
makeGlobalExtentSourceExtent199   static SourceExtent makeGlobalExtent(uint32_t len) {
200     return SourceExtent(0, len, 0, len, 1, 0);
201   }
202 
makeGlobalExtentSourceExtent203   static SourceExtent makeGlobalExtent(uint32_t len, uint32_t lineno,
204                                        uint32_t column) {
205     return SourceExtent(0, len, 0, len, lineno, column);
206   }
207 
208   uint32_t sourceStart = 0;
209   uint32_t sourceEnd = 0;
210   uint32_t toStringStart = 0;
211   uint32_t toStringEnd = 0;
212 
213   // Line and column of |sourceStart_| position.
214   uint32_t lineno = 1;  // 1-indexed.
215   uint32_t column = 0;  // Count of Code Points
216 };
217 
218 class ImmutableScriptFlags : public EnumFlags<ImmutableScriptFlagsEnum> {
219  public:
220   ImmutableScriptFlags() = default;
221 
ImmutableScriptFlags(FieldType rawFlags)222   explicit ImmutableScriptFlags(FieldType rawFlags) : EnumFlags(rawFlags) {}
223 
FieldType()224   operator FieldType() const { return flags_; }
225 };
226 
227 class MutableScriptFlags : public EnumFlags<MutableScriptFlagsEnum> {
228  public:
229   MutableScriptFlags() = default;
230 
231   MutableScriptFlags& operator&=(const FieldType rhs) {
232     flags_ &= rhs;
233     return *this;
234   }
235 
236   MutableScriptFlags& operator|=(const FieldType rhs) {
237     flags_ |= rhs;
238     return *this;
239   }
240 
FieldType()241   operator FieldType() const { return flags_; }
242 };
243 
244 #define GENERIC_FLAGS_READ_ONLY(Field, Enum) \
245   [[nodiscard]] bool hasFlag(Enum flag) const { return Field.hasFlag(flag); }
246 
247 #define GENERIC_FLAGS_READ_WRITE(Field, Enum)                                 \
248   [[nodiscard]] bool hasFlag(Enum flag) const { return Field.hasFlag(flag); } \
249   void setFlag(Enum flag, bool b = true) { Field.setFlag(flag, b); }          \
250   void clearFlag(Enum flag) { Field.clearFlag(flag); }
251 
252 #define GENERIC_FLAG_GETTER(enumName, lowerName, name) \
253   bool lowerName() const { return hasFlag(enumName::name); }
254 
255 #define GENERIC_FLAG_GETTER_SETTER(enumName, lowerName, name) \
256   GENERIC_FLAG_GETTER(enumName, lowerName, name)              \
257   void set##name() { setFlag(enumName::name); }               \
258   void set##name(bool b) { setFlag(enumName::name, b); }      \
259   void clear##name() { clearFlag(enumName::name); }
260 
261 #define IMMUTABLE_SCRIPT_FLAGS_WITH_ACCESSORS(_)                              \
262   _(ImmutableFlags, isForEval, IsForEval)                                     \
263   _(ImmutableFlags, isModule, IsModule)                                       \
264   _(ImmutableFlags, isFunction, IsFunction)                                   \
265   _(ImmutableFlags, selfHosted, SelfHosted)                                   \
266   _(ImmutableFlags, forceStrict, ForceStrict)                                 \
267   _(ImmutableFlags, hasNonSyntacticScope, HasNonSyntacticScope)               \
268   _(ImmutableFlags, noScriptRval, NoScriptRval)                               \
269   _(ImmutableFlags, treatAsRunOnce, TreatAsRunOnce)                           \
270   _(ImmutableFlags, strict, Strict)                                           \
271   _(ImmutableFlags, hasModuleGoal, HasModuleGoal)                             \
272   _(ImmutableFlags, hasInnerFunctions, HasInnerFunctions)                     \
273   _(ImmutableFlags, hasDirectEval, HasDirectEval)                             \
274   _(ImmutableFlags, bindingsAccessedDynamically, BindingsAccessedDynamically) \
275   _(ImmutableFlags, hasCallSiteObj, HasCallSiteObj)                           \
276   _(ImmutableFlags, isAsync, IsAsync)                                         \
277   _(ImmutableFlags, isGenerator, IsGenerator)                                 \
278   _(ImmutableFlags, funHasExtensibleScope, FunHasExtensibleScope)             \
279   _(ImmutableFlags, functionHasThisBinding, FunctionHasThisBinding)           \
280   _(ImmutableFlags, needsHomeObject, NeedsHomeObject)                         \
281   _(ImmutableFlags, isDerivedClassConstructor, IsDerivedClassConstructor)     \
282   _(ImmutableFlags, isSyntheticFunction, IsSyntheticFunction)                 \
283   _(ImmutableFlags, useMemberInitializers, UseMemberInitializers)             \
284   _(ImmutableFlags, hasRest, HasRest)                                         \
285   _(ImmutableFlags, needsFunctionEnvironmentObjects,                          \
286     NeedsFunctionEnvironmentObjects)                                          \
287   _(ImmutableFlags, functionHasExtraBodyVarScope,                             \
288     FunctionHasExtraBodyVarScope)                                             \
289   _(ImmutableFlags, shouldDeclareArguments, ShouldDeclareArguments)           \
290   _(ImmutableFlags, needsArgsObj, NeedsArgsObj)                               \
291   _(ImmutableFlags, hasMappedArgsObj, HasMappedArgsObj)                       \
292   _(ImmutableFlags, isInlinableLargeFunction, IsInlinableLargeFunction)       \
293                                                                               \
294   GeneratorKind generatorKind() const {                                       \
295     return isGenerator() ? GeneratorKind::Generator                           \
296                          : GeneratorKind::NotGenerator;                       \
297   }                                                                           \
298                                                                               \
299   FunctionAsyncKind asyncKind() const {                                       \
300     return isAsync() ? FunctionAsyncKind::AsyncFunction                       \
301                      : FunctionAsyncKind::SyncFunction;                       \
302   }                                                                           \
303                                                                               \
304   bool isRelazifiable() const {                                               \
305     /*                                                                        \
306     ** A script may not be relazifiable if parts of it can be entrained in    \
307     ** interesting ways:                                                      \
308     **  - Scripts with inner-functions or direct-eval (which can add          \
309     **    inner-functions) should not be relazified as their Scopes may be    \
310     **    part of another scope-chain.                                        \
311     **  - Generators and async functions may be re-entered in complex ways so \
312     **    don't discard bytecode. The JIT resume code assumes this.           \
313     **  - Functions with template literals must always return the same object \
314     **    instance so must not discard it by relazifying.                     \
315     */                                                                        \
316     return !hasInnerFunctions() && !hasDirectEval() && !isGenerator() &&      \
317            !isAsync() && !hasCallSiteObj();                                   \
318   }
319 
320 #define RO_IMMUTABLE_SCRIPT_FLAGS(Field)           \
321   using ImmutableFlags = ImmutableScriptFlagsEnum; \
322                                                    \
323   GENERIC_FLAGS_READ_ONLY(Field, ImmutableFlags)   \
324   IMMUTABLE_SCRIPT_FLAGS_WITH_ACCESSORS(GENERIC_FLAG_GETTER)
325 
326 #define MUTABLE_SCRIPT_FLAGS_WITH_ACCESSORS(_)                          \
327   _(MutableFlags, hasRunOnce, HasRunOnce)                               \
328   _(MutableFlags, hasScriptCounts, HasScriptCounts)                     \
329   _(MutableFlags, hasDebugScript, HasDebugScript)                       \
330   _(MutableFlags, allowRelazify, AllowRelazify)                         \
331   _(MutableFlags, spewEnabled, SpewEnabled)                             \
332   _(MutableFlags, needsFinalWarmUpCount, NeedsFinalWarmUpCount)         \
333   _(MutableFlags, failedBoundsCheck, FailedBoundsCheck)                 \
334   _(MutableFlags, hadLICMInvalidation, HadLICMInvalidation)             \
335   _(MutableFlags, hadReorderingBailout, HadReorderingBailout)           \
336   _(MutableFlags, hadEagerTruncationBailout, HadEagerTruncationBailout) \
337   _(MutableFlags, hadUnboxFoldingBailout, HadUnboxFoldingBailout)       \
338   _(MutableFlags, baselineDisabled, BaselineDisabled)                   \
339   _(MutableFlags, ionDisabled, IonDisabled)                             \
340   _(MutableFlags, uninlineable, Uninlineable)                           \
341   _(MutableFlags, failedLexicalCheck, FailedLexicalCheck)               \
342   _(MutableFlags, hadSpeculativePhiBailout, HadSpeculativePhiBailout)
343 
344 #define RW_MUTABLE_SCRIPT_FLAGS(Field)          \
345   using MutableFlags = MutableScriptFlagsEnum;  \
346                                                 \
347   GENERIC_FLAGS_READ_WRITE(Field, MutableFlags) \
348   MUTABLE_SCRIPT_FLAGS_WITH_ACCESSORS(GENERIC_FLAG_GETTER_SETTER)
349 
350 // [SMDOC] JSScript data layout (immutable)
351 //
352 // ImmutableScriptData stores variable-length script data that may be shared
353 // between scripts with the same bytecode, even across different GC Zones.
354 // Abstractly this structure consists of multiple (optional) arrays that are
355 // exposed as mozilla::Span<T>. These arrays exist in a single heap allocation.
356 //
357 // Under the hood, ImmutableScriptData is a fixed-size header class followed
358 // the various array bodies interleaved with metadata to compactly encode the
359 // bounds. These arrays have varying requirements for alignment, performance,
360 // and jit-friendliness which leads to the complex indexing system below.
361 //
362 // Note: The '----' separators are for readability only.
363 //
364 // ----
365 //   <ImmutableScriptData itself>
366 // ----
367 //   (REQUIRED) Flags structure
368 //   (REQUIRED) Array of jsbytecode constituting code()
369 //   (REQUIRED) Array of SrcNote constituting notes()
370 // ----
371 //   (OPTIONAL) Array of uint32_t optional-offsets
372 //  optArrayOffset:
373 // ----
374 //  L0:
375 //   (OPTIONAL) Array of uint32_t constituting resumeOffsets()
376 //  L1:
377 //   (OPTIONAL) Array of ScopeNote constituting scopeNotes()
378 //  L2:
379 //   (OPTIONAL) Array of TryNote constituting tryNotes()
380 //  L3:
381 // ----
382 //
383 // NOTE: The notes() array must have been null-padded such that
384 //       flags/code/notes together have uint32_t alignment.
385 //
386 // The labels shown are recorded as byte-offsets relative to 'this'. This is to
387 // reduce memory as well as make ImmutableScriptData easier to share across
388 // processes.
389 //
390 // The L0/L1/L2/L3 labels indicate the start and end of the optional arrays.
391 // Some of these labels may refer to the same location if the array between
392 // them is empty. Each unique label position has an offset stored in the
393 // optional-offsets table. Note that we also avoid entries for labels that
394 // match 'optArrayOffset'. This saves memory when arrays are empty.
395 //
396 // The flags() data indicates (for each optional array) which entry from the
397 // optional-offsets table marks the *end* of array. The array starts where the
398 // previous array ends (with the first array beginning at 'optArrayOffset').
399 // The optional-offset table is addressed at negative indices from
400 // 'optArrayOffset'.
401 //
402 // In general, the length of each array is computed from subtracting the start
403 // offset of the array from the start offset of the subsequent array. The
404 // notable exception is that bytecode length is stored explicitly.
alignas(uint32_t)405 class alignas(uint32_t) ImmutableScriptData final : public TrailingArray {
406  private:
407   Offset optArrayOffset_ = 0;
408 
409   // Length of bytecode
410   uint32_t codeLength_ = 0;
411 
412  public:
413   // Offset of main entry point from code, after predef'ing prologue.
414   uint32_t mainOffset = 0;
415 
416   // Fixed frame slots.
417   uint32_t nfixed = 0;
418 
419   // Slots plus maximum stack depth.
420   uint32_t nslots = 0;
421 
422   // Index into the gcthings array of the body scope.
423   GCThingIndex bodyScopeIndex;
424 
425   // Number of IC entries to allocate in JitScript for Baseline ICs.
426   uint32_t numICEntries = 0;
427 
428   // ES6 function length.
429   uint16_t funLength = 0;
430 
431   // NOTE: The raw bytes of this structure are used for hashing so use explicit
432   // padding values as needed for predicatable results across compilers.
433   uint16_t padding = 0;
434 
435  private:
436   struct Flags {
437     uint8_t resumeOffsetsEndIndex : 2;
438     uint8_t scopeNotesEndIndex : 2;
439     uint8_t tryNotesEndIndex : 2;
440     uint8_t _unused : 2;
441   };
442   static_assert(sizeof(Flags) == sizeof(uint8_t),
443                 "Structure packing is broken");
444 
445   // Offsets (in bytes) from 'this' to each component array. The delta between
446   // each offset and the next offset is the size of each array and is defined
447   // even if an array is empty.
448   Offset flagOffset() const { return offsetOfCode() - sizeof(Flags); }
449   Offset codeOffset() const { return offsetOfCode(); }
450   Offset noteOffset() const { return offsetOfCode() + codeLength_; }
451   Offset optionalOffsetsOffset() const {
452     // Determine the location to beginning of optional-offsets array by looking
453     // at index for try-notes.
454     //
455     //   optionalOffsetsOffset():
456     //     (OPTIONAL) tryNotesEndOffset
457     //     (OPTIONAL) scopeNotesEndOffset
458     //     (OPTIONAL) resumeOffsetsEndOffset
459     //   optArrayOffset_:
460     //     ....
461     unsigned numOffsets = flags().tryNotesEndIndex;
462     MOZ_ASSERT(numOffsets >= flags().scopeNotesEndIndex);
463     MOZ_ASSERT(numOffsets >= flags().resumeOffsetsEndIndex);
464 
465     return optArrayOffset_ - (numOffsets * sizeof(Offset));
466   }
467   Offset resumeOffsetsOffset() const { return optArrayOffset_; }
468   Offset scopeNotesOffset() const {
469     return getOptionalOffset(flags().resumeOffsetsEndIndex);
470   }
471   Offset tryNotesOffset() const {
472     return getOptionalOffset(flags().scopeNotesEndIndex);
473   }
474   Offset endOffset() const {
475     return getOptionalOffset(flags().tryNotesEndIndex);
476   }
477 
478   void initOptionalArrays(Offset* cursor, uint32_t numResumeOffsets,
479                           uint32_t numScopeNotes, uint32_t numTryNotes);
480 
481   // Initialize to GC-safe state
482   ImmutableScriptData(uint32_t codeLength, uint32_t noteLength,
483                       uint32_t numResumeOffsets, uint32_t numScopeNotes,
484                       uint32_t numTryNotes);
485 
486   void setOptionalOffset(int index, Offset offset) {
487     MOZ_ASSERT(index > 0);
488     MOZ_ASSERT(offset != optArrayOffset_, "Do not store implicit offset");
489     offsetToPointer<Offset>(optArrayOffset_)[-index] = offset;
490   }
491   Offset getOptionalOffset(int index) const {
492     // The index 0 represents (implicitly) the offset 'optArrayOffset_'.
493     if (index == 0) {
494       return optArrayOffset_;
495     }
496 
497     ImmutableScriptData* this_ = const_cast<ImmutableScriptData*>(this);
498     return this_->offsetToPointer<Offset>(optArrayOffset_)[-index];
499   }
500 
501  public:
502   static js::UniquePtr<ImmutableScriptData> new_(
503       JSContext* cx, uint32_t mainOffset, uint32_t nfixed, uint32_t nslots,
504       GCThingIndex bodyScopeIndex, uint32_t numICEntries, bool isFunction,
505       uint16_t funLength, mozilla::Span<const jsbytecode> code,
506       mozilla::Span<const SrcNote> notes,
507       mozilla::Span<const uint32_t> resumeOffsets,
508       mozilla::Span<const ScopeNote> scopeNotes,
509       mozilla::Span<const TryNote> tryNotes);
510 
511   static js::UniquePtr<ImmutableScriptData> new_(
512       JSContext* cx, uint32_t codeLength, uint32_t noteLength,
513       uint32_t numResumeOffsets, uint32_t numScopeNotes, uint32_t numTryNotes);
514 
515   static js::UniquePtr<ImmutableScriptData> new_(JSContext* cx,
516                                                  uint32_t totalSize);
517 
518   uint32_t computedSize();
519 
520  private:
521   static mozilla::CheckedInt<uint32_t> sizeFor(uint32_t codeLength,
522                                                uint32_t noteLength,
523                                                uint32_t numResumeOffsets,
524                                                uint32_t numScopeNotes,
525                                                uint32_t numTryNotes);
526 
527  public:
528   // The code() and note() arrays together maintain an target alignment by
529   // padding the source notes with null. This allows arrays with stricter
530   // alignment requirements to follow them.
531   static constexpr size_t CodeNoteAlign = sizeof(uint32_t);
532 
533   // Compute number of null notes to pad out source notes with.
534   static uint32_t ComputeNotePadding(uint32_t codeLength, uint32_t noteLength) {
535     uint32_t flagLength = sizeof(Flags);
536     uint32_t nullLength =
537         CodeNoteAlign - (flagLength + codeLength + noteLength) % CodeNoteAlign;
538 
539     // The source notes must have at least one null-terminator.
540     MOZ_ASSERT(nullLength >= 1);
541 
542     return nullLength;
543   }
544 
545   // Span over all raw bytes in this struct and its trailing arrays.
546   mozilla::Span<const uint8_t> immutableData() const {
547     size_t allocSize = endOffset();
548     return mozilla::Span{reinterpret_cast<const uint8_t*>(this), allocSize};
549   }
550 
551  private:
552   Flags& flagsRef() { return *offsetToPointer<Flags>(flagOffset()); }
553   const Flags& flags() const {
554     return const_cast<ImmutableScriptData*>(this)->flagsRef();
555   }
556 
557  public:
558   uint32_t codeLength() const { return codeLength_; }
559   jsbytecode* code() { return offsetToPointer<jsbytecode>(codeOffset()); }
560   mozilla::Span<jsbytecode> codeSpan() { return {code(), codeLength()}; }
561 
562   uint32_t noteLength() const {
563     return numElements<SrcNote>(noteOffset(), optionalOffsetsOffset());
564   }
565   SrcNote* notes() { return offsetToPointer<SrcNote>(noteOffset()); }
566   mozilla::Span<SrcNote> notesSpan() { return {notes(), noteLength()}; }
567 
568   mozilla::Span<uint32_t> resumeOffsets() {
569     return mozilla::Span{offsetToPointer<uint32_t>(resumeOffsetsOffset()),
570                          offsetToPointer<uint32_t>(scopeNotesOffset())};
571   }
572   mozilla::Span<ScopeNote> scopeNotes() {
573     return mozilla::Span{offsetToPointer<ScopeNote>(scopeNotesOffset()),
574                          offsetToPointer<ScopeNote>(tryNotesOffset())};
575   }
576   mozilla::Span<TryNote> tryNotes() {
577     return mozilla::Span{offsetToPointer<TryNote>(tryNotesOffset()),
578                          offsetToPointer<TryNote>(endOffset())};
579   }
580 
581   // Expose offsets to the JITs.
582   static constexpr size_t offsetOfCode() {
583     return sizeof(ImmutableScriptData) + sizeof(Flags);
584   }
585   static constexpr size_t offsetOfResumeOffsetsOffset() {
586     // Resume-offsets are the first optional array if they exist. Locate the
587     // array with the 'optArrayOffset_' field.
588     static_assert(sizeof(Offset) == sizeof(uint32_t),
589                   "JIT expect Offset to be uint32_t");
590     return offsetof(ImmutableScriptData, optArrayOffset_);
591   }
592   static constexpr size_t offsetOfNfixed() {
593     return offsetof(ImmutableScriptData, nfixed);
594   }
595   static constexpr size_t offsetOfNslots() {
596     return offsetof(ImmutableScriptData, nslots);
597   }
598   static constexpr size_t offsetOfFunLength() {
599     return offsetof(ImmutableScriptData, funLength);
600   }
601 
602   // ImmutableScriptData has trailing data so isn't copyable or movable.
603   ImmutableScriptData(const ImmutableScriptData&) = delete;
604   ImmutableScriptData& operator=(const ImmutableScriptData&) = delete;
605 };
606 
607 // Wrapper type for ImmutableScriptData to allow sharing across a JSRuntime.
608 //
609 // Note: This is distinct from ImmutableScriptData because it contains a mutable
610 //       ref-count while the ImmutableScriptData may live in read-only memory.
611 //
612 // Note: This is *not* directly inlined into the SharedImmutableScriptDataTable
613 //       because scripts point directly to object and table resizing moves
614 //       entries. This allows for fast finalization by decrementing the
615 //       ref-count directly without doing a hash-table lookup.
616 class SharedImmutableScriptData {
617   // This class is reference counted as follows: each pointer from a JSScript
618   // counts as one reference plus there may be one reference from the shared
619   // script data table.
620   mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent> refCount_ = {};
621 
622  public:
623   bool isExternal = false;
624 
625  private:
626   ImmutableScriptData* isd_ = nullptr;
627 
628   // End of fields.
629 
630   friend class ::JSScript;
631   friend class js::frontend::StencilXDR;
632 
633  public:
634   SharedImmutableScriptData() = default;
635 
~SharedImmutableScriptData()636   ~SharedImmutableScriptData() { reset(); }
637 
638  private:
reset()639   void reset() {
640     if (isd_ && !isExternal) {
641       js_delete(isd_);
642     }
643     isd_ = nullptr;
644   }
645 
646  public:
647   // Hash over the contents of SharedImmutableScriptData and its
648   // ImmutableScriptData.
649   struct Hasher;
650 
refCount()651   uint32_t refCount() const { return refCount_; }
AddRef()652   void AddRef() { refCount_++; }
Release()653   void Release() {
654     MOZ_ASSERT(refCount_ != 0);
655     uint32_t remain = --refCount_;
656     if (remain == 0) {
657       reset();
658       js_free(this);
659     }
660   }
661 
offsetOfISD()662   static constexpr size_t offsetOfISD() {
663     return offsetof(SharedImmutableScriptData, isd_);
664   }
665 
666  private:
667   static SharedImmutableScriptData* create(JSContext* cx);
668 
669  public:
670   static SharedImmutableScriptData* createWith(
671       JSContext* cx, js::UniquePtr<ImmutableScriptData>&& isd);
672 
sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)673   size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
674     size_t isdSize = isExternal ? 0 : mallocSizeOf(isd_);
675     return mallocSizeOf(this) + isdSize;
676   }
677 
678   // SharedImmutableScriptData has trailing data so isn't copyable or movable.
679   SharedImmutableScriptData(const SharedImmutableScriptData&) = delete;
680   SharedImmutableScriptData& operator=(const SharedImmutableScriptData&) =
681       delete;
682 
683   static bool shareScriptData(JSContext* cx,
684                               RefPtr<SharedImmutableScriptData>& sisd);
685 
immutableDataLength()686   size_t immutableDataLength() const { return isd_->immutableData().Length(); }
nfixed()687   uint32_t nfixed() const { return isd_->nfixed; }
688 
get()689   ImmutableScriptData* get() { return isd_; }
690 
setOwn(js::UniquePtr<ImmutableScriptData> && isd)691   void setOwn(js::UniquePtr<ImmutableScriptData>&& isd) {
692     MOZ_ASSERT(!isd_);
693     isd_ = isd.release();
694     isExternal = false;
695   }
696 
setExternal(ImmutableScriptData * isd)697   void setExternal(ImmutableScriptData* isd) {
698     MOZ_ASSERT(!isd_);
699     isd_ = isd;
700     isExternal = true;
701   }
702 };
703 
704 // Matches SharedImmutableScriptData objects that have the same atoms as well as
705 // contain the same bytes in their ImmutableScriptData.
706 struct SharedImmutableScriptData::Hasher {
707   using Lookup = RefPtr<SharedImmutableScriptData>;
708 
hashHasher709   static mozilla::HashNumber hash(const Lookup& l) {
710     mozilla::Span<const uint8_t> immutableData = l->isd_->immutableData();
711     return mozilla::HashBytes(immutableData.data(), immutableData.size());
712   }
713 
matchHasher714   static bool match(SharedImmutableScriptData* entry, const Lookup& lookup) {
715     return (entry->isd_->immutableData() == lookup->isd_->immutableData());
716   }
717 };
718 
719 using SharedImmutableScriptDataTable =
720     mozilla::HashSet<SharedImmutableScriptData*,
721                      SharedImmutableScriptData::Hasher, SystemAllocPolicy>;
722 
723 struct MemberInitializers {
724   static constexpr size_t NumBits = 31;
725   static constexpr uint32_t MaxInitializers = BitMask(NumBits);
726 
727 #ifdef DEBUG
728   bool valid = false;
729 #endif
730 
731   bool hasPrivateBrand : 1;
732 
733   // This struct will eventually have a vector of constant values for optimizing
734   // field initializers.
735   uint32_t numMemberInitializers : NumBits;
736 
MemberInitializersMemberInitializers737   MemberInitializers(bool hasPrivateBrand, uint32_t numMemberInitializers)
738       :
739 #ifdef DEBUG
740         valid(true),
741 #endif
742         hasPrivateBrand(hasPrivateBrand),
743         numMemberInitializers(numMemberInitializers) {
744     MOZ_ASSERT(
745         this->numMemberInitializers == numMemberInitializers,
746         "numMemberInitializers should easily fit in the 31-bit bitfield");
747   }
748 
InvalidMemberInitializers749   static MemberInitializers Invalid() { return MemberInitializers(); }
750 
751   // Singleton to use for class constructors that do not have to initialize any
752   // fields. This is used when we elide the trivial data but still need a valid
753   // set to stop scope walking.
EmptyMemberInitializers754   static const MemberInitializers& Empty() {
755     static const MemberInitializers zeroInitializers(false, 0);
756     return zeroInitializers;
757   }
758 
serializeMemberInitializers759   uint32_t serialize() const {
760     return (hasPrivateBrand << NumBits) | numMemberInitializers;
761   }
762 
deserializeMemberInitializers763   static MemberInitializers deserialize(uint32_t bits) {
764     return MemberInitializers((bits & Bit(NumBits)) != 0,
765                               bits & BitMask(NumBits));
766   }
767 
768  private:
MemberInitializersMemberInitializers769   MemberInitializers()
770       :
771 #ifdef DEBUG
772         valid(false),
773 #endif
774         hasPrivateBrand(false),
775         numMemberInitializers(0) {
776   }
777 };
778 
779 }  // namespace js
780 
781 #endif /* vm_SharedStencil_h */
782