tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SharedStencil.h (34717B)


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