tor-browser

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

JSScript.cpp (124676B)


      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 /*
      8 * JS script operations.
      9 */
     10 
     11 #include "vm/JSScript-inl.h"
     12 
     13 #include "mozilla/ArrayUtils.h"
     14 #include "mozilla/CheckedInt.h"
     15 #include "mozilla/DebugOnly.h"
     16 #include "mozilla/Maybe.h"
     17 #include "mozilla/MemoryReporting.h"
     18 #include "mozilla/ScopeExit.h"
     19 #include "mozilla/Span.h"  // mozilla::{Span,Span}
     20 #include "mozilla/Sprintf.h"
     21 #include "mozilla/Utf8.h"
     22 #include "mozilla/Vector.h"
     23 
     24 #include <algorithm>
     25 #include <new>
     26 #include <string.h>
     27 #include <utility>
     28 
     29 #include "jstypes.h"
     30 
     31 #include "frontend/BytecodeSection.h"
     32 #include "frontend/CompilationStencil.h"  // frontend::CompilationStencil, frontend::InitialStencilAndDelazifications
     33 #include "frontend/FrontendContext.h"  // AutoReportFrontendContext
     34 #include "frontend/ParseContext.h"
     35 #include "frontend/SourceNotes.h"  // SrcNote, SrcNoteType, SrcNoteIterator
     36 #include "frontend/Stencil.h"  // DumpFunctionFlagsItems, DumpImmutableScriptFlags
     37 #include "frontend/StencilXdr.h"  // XDRStencilEncoder
     38 #include "gc/GCContext.h"
     39 #include "jit/BaselineJIT.h"
     40 #include "jit/CacheIRHealth.h"
     41 #include "jit/Ion.h"
     42 #include "jit/IonScript.h"
     43 #include "jit/JitCode.h"
     44 #include "jit/JitOptions.h"
     45 #include "jit/JitRuntime.h"
     46 #include "js/CharacterEncoding.h"  // JS_EncodeStringToUTF8
     47 #include "js/ColumnNumber.h"  // JS::LimitedColumnNumberOneOrigin, JS::ColumnNumberOneOrigin, JS::ColumnNumberOffset
     48 #include "js/CompileOptions.h"
     49 #include "js/experimental/SourceHook.h"
     50 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     51 #include "js/HeapAPI.h"               // JS::GCCellPtr
     52 #include "js/MemoryMetrics.h"
     53 #include "js/Printer.h"  // js::GenericPrinter, js::Fprinter, js::Sprinter, js::QuoteString
     54 #include "js/Transcoding.h"
     55 #include "js/UniquePtr.h"
     56 #include "js/Utility.h"  // JS::UniqueChars
     57 #include "js/Value.h"    // JS::Value
     58 #include "util/Poison.h"
     59 #include "util/StringBuilder.h"
     60 #include "util/Text.h"
     61 #include "vm/BigIntType.h"  // JS::BigInt
     62 #include "vm/BytecodeIterator.h"
     63 #include "vm/BytecodeLocation.h"
     64 #include "vm/BytecodeUtil.h"  // Disassemble
     65 #include "vm/Compression.h"
     66 #include "vm/HelperThreadState.h"  // js::RunPendingSourceCompressions
     67 #include "vm/JSFunction.h"
     68 #include "vm/JSObject.h"
     69 #include "vm/JSONPrinter.h"  // JSONPrinter
     70 #include "vm/Opcodes.h"
     71 #include "vm/PortableBaselineInterpret.h"
     72 #include "vm/Scope.h"  // Scope
     73 #include "vm/SharedImmutableStringsCache.h"
     74 #include "vm/StencilEnums.h"  // TryNote, TryNoteKind, ScopeNote
     75 #include "vm/StringType.h"    // JSString, JSAtom
     76 #include "vm/Time.h"          // AutoIncrementalTimer
     77 #include "vm/ToSource.h"      // JS::ValueToSource
     78 #ifdef MOZ_VTUNE
     79 #  include "vtune/VTuneWrapper.h"
     80 #endif
     81 
     82 #include "gc/Marking-inl.h"
     83 #include "vm/BytecodeIterator-inl.h"
     84 #include "vm/BytecodeLocation-inl.h"
     85 #include "vm/Compartment-inl.h"
     86 #include "vm/JSContext-inl.h"
     87 #include "vm/JSObject-inl.h"
     88 #include "vm/SharedImmutableStringsCache-inl.h"
     89 #include "vm/Stack-inl.h"
     90 
     91 using namespace js;
     92 
     93 using mozilla::CheckedInt;
     94 using mozilla::Maybe;
     95 using mozilla::PointerRangeSize;
     96 using mozilla::Utf8Unit;
     97 
     98 using JS::ReadOnlyCompileOptions;
     99 using JS::SourceText;
    100 
    101 bool js::BaseScript::isUsingInterpreterTrampoline(JSRuntime* rt) const {
    102  return jitCodeRaw() == rt->jitRuntime()->interpreterStub().value;
    103 }
    104 
    105 js::ScriptSource* js::BaseScript::maybeForwardedScriptSource() const {
    106  return MaybeForwarded(sourceObject())->source();
    107 }
    108 
    109 void js::BaseScript::setEnclosingScript(BaseScript* enclosingScript) {
    110  MOZ_ASSERT(enclosingScript);
    111  warmUpData_.initEnclosingScript(enclosingScript);
    112 }
    113 
    114 void js::BaseScript::setEnclosingScope(Scope* enclosingScope) {
    115  if (warmUpData_.isEnclosingScript()) {
    116    warmUpData_.clearEnclosingScript();
    117  }
    118 
    119  MOZ_ASSERT(enclosingScope);
    120  warmUpData_.initEnclosingScope(enclosingScope);
    121 }
    122 
    123 void js::BaseScript::finalize(JS::GCContext* gcx) {
    124  // Scripts with bytecode may have optional data stored in per-runtime or
    125  // per-zone maps. Note that a failed compilation must not have entries since
    126  // the script itself will not be marked as having bytecode.
    127  if (hasBytecode()) {
    128    JSScript* script = this->asJSScript();
    129 
    130    if (coverage::IsLCovEnabled()) {
    131      coverage::CollectScriptCoverage(script, true);
    132    }
    133 
    134    script->destroyScriptCounts();
    135  }
    136 
    137  {
    138    JSRuntime* rt = gcx->runtime();
    139    if (rt->hasJitRuntime() && rt->jitRuntime()->hasInterpreterEntryMap()) {
    140      rt->jitRuntime()->getInterpreterEntryMap()->remove(this);
    141    }
    142 
    143    rt->geckoProfiler().onScriptFinalized(this);
    144  }
    145 
    146 #ifdef MOZ_VTUNE
    147  if (zone()->scriptVTuneIdMap) {
    148    // Note: we should only get here if the VTune JIT profiler is running.
    149    zone()->scriptVTuneIdMap->remove(this);
    150  }
    151 #endif
    152 
    153  if (warmUpData_.isJitScript()) {
    154    JSScript* script = this->asJSScript();
    155 #ifdef JS_CACHEIR_SPEW
    156    maybeUpdateWarmUpCount(script);
    157 #endif
    158    script->releaseJitScriptOnFinalize(gcx);
    159  }
    160 
    161 #ifdef JS_CACHEIR_SPEW
    162  if (hasBytecode()) {
    163    maybeSpewScriptFinalWarmUpCount(this->asJSScript());
    164  }
    165 #endif
    166 
    167  if (data_) {
    168    // We don't need to triger any barriers here, just free the memory.
    169    size_t size = data_->allocationSize();
    170    AlwaysPoison(data_, JS_POISONED_JSSCRIPT_DATA_PATTERN, size,
    171                 MemCheckKind::MakeNoAccess);
    172    gcx->free_(this, data_, size, MemoryUse::ScriptPrivateData);
    173  }
    174 
    175  freeSharedData();
    176 }
    177 
    178 js::Scope* js::BaseScript::releaseEnclosingScope() {
    179  Scope* enclosing = warmUpData_.toEnclosingScope();
    180  warmUpData_.clearEnclosingScope();
    181  return enclosing;
    182 }
    183 
    184 void js::BaseScript::swapData(UniquePtr<PrivateScriptData>& other) {
    185  if (data_) {
    186    RemoveCellMemory(this, data_->allocationSize(),
    187                     MemoryUse::ScriptPrivateData);
    188  }
    189 
    190  PrivateScriptData* old = data_;
    191  data_.set(zone(), other.release());
    192  other.reset(old);
    193 
    194  if (data_) {
    195    AddCellMemory(this, data_->allocationSize(), MemoryUse::ScriptPrivateData);
    196  }
    197 }
    198 
    199 js::Scope* js::BaseScript::enclosingScope() const {
    200  MOZ_ASSERT(!warmUpData_.isEnclosingScript(),
    201             "Enclosing scope is not computed yet");
    202 
    203  if (warmUpData_.isEnclosingScope()) {
    204    return warmUpData_.toEnclosingScope();
    205  }
    206 
    207  MOZ_ASSERT(data_, "Script doesn't seem to be compiled");
    208 
    209  return gcthings()[js::GCThingIndex::outermostScopeIndex()]
    210      .as<Scope>()
    211      .enclosing();
    212 }
    213 
    214 size_t JSScript::numAlwaysLiveFixedSlots() const {
    215  if (bodyScope()->is<js::FunctionScope>()) {
    216    return bodyScope()->as<js::FunctionScope>().nextFrameSlot();
    217  }
    218  if (bodyScope()->is<js::ModuleScope>()) {
    219    return bodyScope()->as<js::ModuleScope>().nextFrameSlot();
    220  }
    221  if (bodyScope()->is<js::EvalScope>() &&
    222      bodyScope()->kind() == ScopeKind::StrictEval) {
    223    return bodyScope()->as<js::EvalScope>().nextFrameSlot();
    224  }
    225  return 0;
    226 }
    227 
    228 unsigned JSScript::numArgs() const {
    229  if (bodyScope()->is<js::FunctionScope>()) {
    230    return bodyScope()->as<js::FunctionScope>().numPositionalFormalParameters();
    231  }
    232  return 0;
    233 }
    234 
    235 bool JSScript::functionHasParameterExprs() const {
    236  // Only functions have parameters.
    237  js::Scope* scope = bodyScope();
    238  if (!scope->is<js::FunctionScope>()) {
    239    return false;
    240  }
    241  return scope->as<js::FunctionScope>().hasParameterExprs();
    242 }
    243 
    244 bool JSScript::isModule() const { return bodyScope()->is<js::ModuleScope>(); }
    245 
    246 js::ModuleObject* JSScript::module() const {
    247  MOZ_ASSERT(isModule());
    248  return bodyScope()->as<js::ModuleScope>().module();
    249 }
    250 
    251 bool JSScript::isGlobalCode() const {
    252  return bodyScope()->is<js::GlobalScope>();
    253 }
    254 
    255 js::VarScope* JSScript::functionExtraBodyVarScope() const {
    256  MOZ_ASSERT(functionHasExtraBodyVarScope());
    257  for (JS::GCCellPtr gcThing : gcthings()) {
    258    if (!gcThing.is<js::Scope>()) {
    259      continue;
    260    }
    261    js::Scope* scope = &gcThing.as<js::Scope>();
    262    if (scope->kind() == js::ScopeKind::FunctionBodyVar) {
    263      return &scope->as<js::VarScope>();
    264    }
    265  }
    266  MOZ_CRASH("Function extra body var scope not found");
    267 }
    268 
    269 bool JSScript::needsBodyEnvironment() const {
    270  for (JS::GCCellPtr gcThing : gcthings()) {
    271    if (!gcThing.is<js::Scope>()) {
    272      continue;
    273    }
    274    js::Scope* scope = &gcThing.as<js::Scope>();
    275    if (ScopeKindIsInBody(scope->kind()) && scope->hasEnvironment()) {
    276      return true;
    277    }
    278  }
    279  return false;
    280 }
    281 
    282 bool JSScript::isDirectEvalInFunction() const {
    283  if (!isForEval()) {
    284    return false;
    285  }
    286  return bodyScope()->hasOnChain(js::ScopeKind::Function);
    287 }
    288 
    289 // Initialize the optional arrays in the trailing allocation. This is a set of
    290 // offsets that delimit each optional array followed by the arrays themselves.
    291 // See comment before 'ImmutableScriptData' for more details.
    292 void ImmutableScriptData::initOptionalArrays(Offset* pcursor,
    293                                             uint32_t numResumeOffsets,
    294                                             uint32_t numScopeNotes,
    295                                             uint32_t numTryNotes) {
    296  Offset cursor = (*pcursor);
    297 
    298  // The byte arrays must have already been padded.
    299  MOZ_ASSERT(isAlignedOffset<CodeNoteAlign>(cursor),
    300             "Bytecode and source notes should be padded to keep alignment");
    301 
    302  // Each non-empty optional array needs will need an offset to its end.
    303  unsigned numOptionalArrays = unsigned(numResumeOffsets > 0) +
    304                               unsigned(numScopeNotes > 0) +
    305                               unsigned(numTryNotes > 0);
    306 
    307  // Default-initialize the optional-offsets.
    308  initElements<Offset>(cursor, numOptionalArrays);
    309  cursor += numOptionalArrays * sizeof(Offset);
    310 
    311  // Offset between optional-offsets table and the optional arrays. This is
    312  // later used to access the optional-offsets table as well as first optional
    313  // array.
    314  optArrayOffset_ = cursor;
    315 
    316  // Each optional array that follows must store an end-offset in the offset
    317  // table. Assign table entries by using this 'offsetIndex'. The index 0 is
    318  // reserved for implicit value 'optArrayOffset'.
    319  int offsetIndex = 0;
    320 
    321  // Default-initialize optional 'resumeOffsets'.
    322  MOZ_ASSERT(resumeOffsetsOffset() == cursor);
    323  if (numResumeOffsets > 0) {
    324    initElements<uint32_t>(cursor, numResumeOffsets);
    325    cursor += numResumeOffsets * sizeof(uint32_t);
    326    setOptionalOffset(++offsetIndex, cursor);
    327  }
    328  flagsRef().resumeOffsetsEndIndex = offsetIndex;
    329 
    330  // Default-initialize optional 'scopeNotes'.
    331  MOZ_ASSERT(scopeNotesOffset() == cursor);
    332  if (numScopeNotes > 0) {
    333    initElements<ScopeNote>(cursor, numScopeNotes);
    334    cursor += numScopeNotes * sizeof(ScopeNote);
    335    setOptionalOffset(++offsetIndex, cursor);
    336  }
    337  flagsRef().scopeNotesEndIndex = offsetIndex;
    338 
    339  // Default-initialize optional 'tryNotes'
    340  MOZ_ASSERT(tryNotesOffset() == cursor);
    341  if (numTryNotes > 0) {
    342    initElements<TryNote>(cursor, numTryNotes);
    343    cursor += numTryNotes * sizeof(TryNote);
    344    setOptionalOffset(++offsetIndex, cursor);
    345  }
    346  flagsRef().tryNotesEndIndex = offsetIndex;
    347 
    348  MOZ_ASSERT(endOffset() == cursor);
    349  (*pcursor) = cursor;
    350 }
    351 
    352 ImmutableScriptData::ImmutableScriptData(uint32_t codeLength,
    353                                         uint32_t noteLength,
    354                                         uint32_t numResumeOffsets,
    355                                         uint32_t numScopeNotes,
    356                                         uint32_t numTryNotes)
    357    : codeLength_(codeLength) {
    358  // Variable-length data begins immediately after ImmutableScriptData itself.
    359  Offset cursor = sizeof(ImmutableScriptData);
    360 
    361  // The following arrays are byte-aligned with additional padding to ensure
    362  // that together they maintain uint32_t-alignment.
    363  {
    364    MOZ_ASSERT(isAlignedOffset<CodeNoteAlign>(cursor));
    365 
    366    // Zero-initialize 'flags'
    367    MOZ_ASSERT(isAlignedOffset<Flags>(cursor));
    368    new (offsetToPointer<void>(cursor)) Flags{};
    369    cursor += sizeof(Flags);
    370 
    371    initElements<jsbytecode>(cursor, codeLength);
    372    cursor += codeLength * sizeof(jsbytecode);
    373 
    374    initElements<SrcNote>(cursor, noteLength);
    375    cursor += noteLength * sizeof(SrcNote);
    376 
    377    MOZ_ASSERT(isAlignedOffset<CodeNoteAlign>(cursor));
    378  }
    379 
    380  // Initialization for remaining arrays.
    381  initOptionalArrays(&cursor, numResumeOffsets, numScopeNotes, numTryNotes);
    382 
    383  // Check that we correctly recompute the expected values.
    384  MOZ_ASSERT(this->codeLength() == codeLength);
    385  MOZ_ASSERT(this->noteLength() == noteLength);
    386 
    387  // Sanity check
    388  MOZ_ASSERT(endOffset() == cursor);
    389 }
    390 
    391 void js::FillImmutableFlagsFromCompileOptionsForTopLevel(
    392    const ReadOnlyCompileOptions& options, ImmutableScriptFlags& flags) {
    393  using ImmutableFlags = ImmutableScriptFlagsEnum;
    394 
    395  js::FillImmutableFlagsFromCompileOptionsForFunction(options, flags);
    396 
    397  flags.setFlag(ImmutableFlags::TreatAsRunOnce, options.isRunOnce);
    398  flags.setFlag(ImmutableFlags::NoScriptRval, options.noScriptRval);
    399 }
    400 
    401 void js::FillImmutableFlagsFromCompileOptionsForFunction(
    402    const ReadOnlyCompileOptions& options, ImmutableScriptFlags& flags) {
    403  using ImmutableFlags = ImmutableScriptFlagsEnum;
    404 
    405  flags.setFlag(ImmutableFlags::SelfHosted, options.selfHostingMode);
    406  flags.setFlag(ImmutableFlags::ForceStrict, options.forceStrictMode());
    407  flags.setFlag(ImmutableFlags::HasNonSyntacticScope,
    408                options.nonSyntacticScope);
    409 }
    410 
    411 // Check if flags matches to compile options for flags set by
    412 // FillImmutableFlagsFromCompileOptionsForTopLevel above.
    413 bool js::CheckCompileOptionsMatch(const ReadOnlyCompileOptions& options,
    414                                  ImmutableScriptFlags flags) {
    415  using ImmutableFlags = ImmutableScriptFlagsEnum;
    416 
    417  bool selfHosted = !!(flags & uint32_t(ImmutableFlags::SelfHosted));
    418  bool forceStrict = !!(flags & uint32_t(ImmutableFlags::ForceStrict));
    419  bool hasNonSyntacticScope =
    420      !!(flags & uint32_t(ImmutableFlags::HasNonSyntacticScope));
    421  bool noScriptRval = !!(flags & uint32_t(ImmutableFlags::NoScriptRval));
    422  bool treatAsRunOnce = !!(flags & uint32_t(ImmutableFlags::TreatAsRunOnce));
    423 
    424  return options.selfHostingMode == selfHosted &&
    425         options.noScriptRval == noScriptRval &&
    426         options.isRunOnce == treatAsRunOnce &&
    427         options.forceStrictMode() == forceStrict &&
    428         options.nonSyntacticScope == hasNonSyntacticScope;
    429 }
    430 
    431 JS_PUBLIC_API bool JS::CheckCompileOptionsMatch(
    432    const ReadOnlyCompileOptions& options, JSScript* script) {
    433  return js::CheckCompileOptionsMatch(options, script->immutableFlags());
    434 }
    435 
    436 bool JSScript::initScriptCounts(JSContext* cx) {
    437  MOZ_ASSERT(!hasScriptCounts());
    438 
    439  // Record all pc which are the first instruction of a basic block.
    440  mozilla::Vector<jsbytecode*, 16, SystemAllocPolicy> jumpTargets;
    441 
    442  js::BytecodeLocation main = mainLocation();
    443  AllBytecodesIterable iterable(this);
    444  for (auto& loc : iterable) {
    445    if (loc.isJumpTarget() || loc == main) {
    446      if (!jumpTargets.append(loc.toRawBytecode())) {
    447        ReportOutOfMemory(cx);
    448        return false;
    449      }
    450    }
    451  }
    452 
    453  // Initialize all PCCounts counters to 0.
    454  ScriptCounts::PCCountsVector base;
    455  if (!base.reserve(jumpTargets.length())) {
    456    ReportOutOfMemory(cx);
    457    return false;
    458  }
    459 
    460  for (size_t i = 0; i < jumpTargets.length(); i++) {
    461    base.infallibleEmplaceBack(pcToOffset(jumpTargets[i]));
    462  }
    463 
    464  // Create zone's scriptCountsMap if necessary.
    465  if (!zone()->scriptCountsMap) {
    466    auto map = cx->make_unique<ScriptCountsMap>();
    467    if (!map) {
    468      return false;
    469    }
    470 
    471    zone()->scriptCountsMap = std::move(map);
    472  }
    473 
    474  // Allocate the ScriptCounts.
    475  UniqueScriptCounts sc = cx->make_unique<ScriptCounts>(std::move(base));
    476  if (!sc) {
    477    return false;
    478  }
    479 
    480  MOZ_ASSERT(this->hasBytecode());
    481 
    482  // Register the current ScriptCounts in the zone's map.
    483  if (!zone()->scriptCountsMap->putNew(this, std::move(sc))) {
    484    ReportOutOfMemory(cx);
    485    return false;
    486  }
    487 
    488  // safe to set this;  we can't fail after this point.
    489  setHasScriptCounts();
    490 
    491  // Enable interrupts in any interpreter frames running on this script. This
    492  // is used to let the interpreter increment the PCCounts, if present.
    493  for (ActivationIterator iter(cx); !iter.done(); ++iter) {
    494    if (iter->isInterpreter()) {
    495      iter->asInterpreter()->enableInterruptsIfRunning(this);
    496    }
    497  }
    498 
    499  return true;
    500 }
    501 
    502 static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript* script) {
    503  MOZ_ASSERT(script->hasScriptCounts());
    504  ScriptCountsMap::Ptr p = script->zone()->scriptCountsMap->lookup(script);
    505  MOZ_ASSERT(p);
    506  return p;
    507 }
    508 
    509 ScriptCounts& JSScript::getScriptCounts() {
    510  ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
    511  return *p->value();
    512 }
    513 
    514 js::PCCounts* ScriptCounts::maybeGetPCCounts(size_t offset) {
    515  PCCounts searched = PCCounts(offset);
    516  PCCounts* elem =
    517      std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
    518  if (elem == pcCounts_.end() || elem->pcOffset() != offset) {
    519    return nullptr;
    520  }
    521  return elem;
    522 }
    523 
    524 const js::PCCounts* ScriptCounts::maybeGetPCCounts(size_t offset) const {
    525  PCCounts searched = PCCounts(offset);
    526  const PCCounts* elem =
    527      std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
    528  if (elem == pcCounts_.end() || elem->pcOffset() != offset) {
    529    return nullptr;
    530  }
    531  return elem;
    532 }
    533 
    534 js::PCCounts* ScriptCounts::getImmediatePrecedingPCCounts(size_t offset) {
    535  PCCounts searched = PCCounts(offset);
    536  PCCounts* elem =
    537      std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
    538  if (elem == pcCounts_.end()) {
    539    return &pcCounts_.back();
    540  }
    541  if (elem->pcOffset() == offset) {
    542    return elem;
    543  }
    544  if (elem != pcCounts_.begin()) {
    545    return elem - 1;
    546  }
    547  return nullptr;
    548 }
    549 
    550 const js::PCCounts* ScriptCounts::maybeGetThrowCounts(size_t offset) const {
    551  PCCounts searched = PCCounts(offset);
    552  const PCCounts* elem =
    553      std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
    554  if (elem == throwCounts_.end() || elem->pcOffset() != offset) {
    555    return nullptr;
    556  }
    557  return elem;
    558 }
    559 
    560 const js::PCCounts* ScriptCounts::getImmediatePrecedingThrowCounts(
    561    size_t offset) const {
    562  PCCounts searched = PCCounts(offset);
    563  const PCCounts* elem =
    564      std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
    565  if (elem == throwCounts_.end()) {
    566    if (throwCounts_.begin() == throwCounts_.end()) {
    567      return nullptr;
    568    }
    569    return &throwCounts_.back();
    570  }
    571  if (elem->pcOffset() == offset) {
    572    return elem;
    573  }
    574  if (elem != throwCounts_.begin()) {
    575    return elem - 1;
    576  }
    577  return nullptr;
    578 }
    579 
    580 js::PCCounts* ScriptCounts::getThrowCounts(size_t offset) {
    581  PCCounts searched = PCCounts(offset);
    582  PCCounts* elem =
    583      std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
    584  if (elem == throwCounts_.end() || elem->pcOffset() != offset) {
    585    elem = throwCounts_.insert(elem, searched);
    586  }
    587  return elem;
    588 }
    589 
    590 size_t ScriptCounts::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
    591  size_t size = mallocSizeOf(this);
    592  size += pcCounts_.sizeOfExcludingThis(mallocSizeOf);
    593  size += throwCounts_.sizeOfExcludingThis(mallocSizeOf);
    594  if (ionCounts_) {
    595    size += ionCounts_->sizeOfIncludingThis(mallocSizeOf);
    596  }
    597  return size;
    598 }
    599 
    600 js::PCCounts* JSScript::maybeGetPCCounts(jsbytecode* pc) {
    601  MOZ_ASSERT(containsPC(pc));
    602  return getScriptCounts().maybeGetPCCounts(pcToOffset(pc));
    603 }
    604 
    605 const js::PCCounts* JSScript::maybeGetThrowCounts(jsbytecode* pc) {
    606  MOZ_ASSERT(containsPC(pc));
    607  return getScriptCounts().maybeGetThrowCounts(pcToOffset(pc));
    608 }
    609 
    610 js::PCCounts* JSScript::getThrowCounts(jsbytecode* pc) {
    611  MOZ_ASSERT(containsPC(pc));
    612  return getScriptCounts().getThrowCounts(pcToOffset(pc));
    613 }
    614 
    615 uint64_t JSScript::getHitCount(jsbytecode* pc) {
    616  MOZ_ASSERT(containsPC(pc));
    617  if (pc < main()) {
    618    pc = main();
    619  }
    620 
    621  ScriptCounts& sc = getScriptCounts();
    622  size_t targetOffset = pcToOffset(pc);
    623  const js::PCCounts* baseCount =
    624      sc.getImmediatePrecedingPCCounts(targetOffset);
    625  if (!baseCount) {
    626    return 0;
    627  }
    628  if (baseCount->pcOffset() == targetOffset) {
    629    return baseCount->numExec();
    630  }
    631  MOZ_ASSERT(baseCount->pcOffset() < targetOffset);
    632  uint64_t count = baseCount->numExec();
    633  do {
    634    const js::PCCounts* throwCount =
    635        sc.getImmediatePrecedingThrowCounts(targetOffset);
    636    if (!throwCount) {
    637      return count;
    638    }
    639    if (throwCount->pcOffset() <= baseCount->pcOffset()) {
    640      return count;
    641    }
    642    count -= throwCount->numExec();
    643    targetOffset = throwCount->pcOffset() - 1;
    644  } while (true);
    645 }
    646 
    647 void JSScript::addIonCounts(jit::IonScriptCounts* ionCounts) {
    648  ScriptCounts& sc = getScriptCounts();
    649  if (sc.ionCounts_) {
    650    ionCounts->setPrevious(sc.ionCounts_);
    651  }
    652  sc.ionCounts_ = ionCounts;
    653 }
    654 
    655 jit::IonScriptCounts* JSScript::getIonCounts() {
    656  return getScriptCounts().ionCounts_;
    657 }
    658 
    659 void JSScript::releaseScriptCounts(ScriptCounts* counts) {
    660  ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
    661  *counts = std::move(*p->value().get());
    662  zone()->scriptCountsMap->remove(p);
    663  clearHasScriptCounts();
    664 }
    665 
    666 void JSScript::destroyScriptCounts() {
    667  if (hasScriptCounts()) {
    668    ScriptCounts scriptCounts;
    669    releaseScriptCounts(&scriptCounts);
    670  }
    671 }
    672 
    673 void JSScript::resetScriptCounts() {
    674  if (!hasScriptCounts()) {
    675    return;
    676  }
    677 
    678  ScriptCounts& sc = getScriptCounts();
    679 
    680  for (PCCounts& elem : sc.pcCounts_) {
    681    elem.numExec() = 0;
    682  }
    683 
    684  for (PCCounts& elem : sc.throwCounts_) {
    685    elem.numExec() = 0;
    686  }
    687 }
    688 
    689 void ScriptSourceObject::finalize(JS::GCContext* gcx, JSObject* obj) {
    690  MOZ_ASSERT(gcx->onMainThread());
    691  ScriptSourceObject* sso = &obj->as<ScriptSourceObject>();
    692  sso->source()->Release();
    693 
    694  // Clear the private value, calling the release hook if necessary.
    695  sso->setPrivate(gcx->runtime(), UndefinedValue());
    696 
    697  sso->clearStencils();
    698 }
    699 
    700 static const JSClassOps ScriptSourceObjectClassOps = {
    701    nullptr,                       // addProperty
    702    nullptr,                       // delProperty
    703    nullptr,                       // enumerate
    704    nullptr,                       // newEnumerate
    705    nullptr,                       // resolve
    706    nullptr,                       // mayResolve
    707    ScriptSourceObject::finalize,  // finalize
    708    nullptr,                       // call
    709    nullptr,                       // construct
    710    nullptr,                       // trace
    711 };
    712 
    713 const JSClass ScriptSourceObject::class_ = {
    714    "ScriptSource",
    715    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_FOREGROUND_FINALIZE |
    716        JSCLASS_SLOT0_IS_NSISUPPORTS,
    717    &ScriptSourceObjectClassOps,
    718 };
    719 
    720 ScriptSourceObject* ScriptSourceObject::create(JSContext* cx,
    721                                               ScriptSource* source) {
    722  ScriptSourceObject* obj =
    723      NewObjectWithGivenProto<ScriptSourceObject>(cx, nullptr);
    724  if (!obj) {
    725    return nullptr;
    726  }
    727 
    728  // The matching decref is in ScriptSourceObject::finalize.
    729  obj->initReservedSlot(SOURCE_SLOT, PrivateValue(do_AddRef(source).take()));
    730 
    731  // The slots below should be populated by a call to initFromOptions. Poison
    732  // them.
    733  obj->initReservedSlot(ELEMENT_PROPERTY_SLOT, MagicValue(JS_GENERIC_MAGIC));
    734  obj->initReservedSlot(INTRODUCTION_SCRIPT_SLOT, MagicValue(JS_GENERIC_MAGIC));
    735 
    736  obj->initReservedSlot(STENCILS_SLOT, UndefinedValue());
    737 
    738  return obj;
    739 }
    740 
    741 [[nodiscard]] static bool MaybeValidateFilename(
    742    JSContext* cx, Handle<ScriptSourceObject*> sso,
    743    const JS::InstantiateOptions& options) {
    744  if (!gFilenameValidationCallback) {
    745    return true;
    746  }
    747 
    748  const char* filename = sso->source()->filename();
    749  if (!filename || options.skipFilenameValidation) {
    750    return true;
    751  }
    752 
    753  if (gFilenameValidationCallback(cx, filename)) {
    754    return true;
    755  }
    756 
    757  const char* utf8Filename;
    758  if (mozilla::IsUtf8(mozilla::MakeStringSpan(filename))) {
    759    utf8Filename = filename;
    760  } else {
    761    utf8Filename = "(invalid UTF-8 filename)";
    762  }
    763  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNSAFE_FILENAME,
    764                           utf8Filename);
    765  return false;
    766 }
    767 
    768 /* static */
    769 bool ScriptSourceObject::initFromOptions(
    770    JSContext* cx, Handle<ScriptSourceObject*> source,
    771    const JS::InstantiateOptions& options) {
    772  cx->releaseCheck(source);
    773  MOZ_ASSERT(
    774      source->getReservedSlot(ELEMENT_PROPERTY_SLOT).isMagic(JS_GENERIC_MAGIC));
    775  MOZ_ASSERT(source->getReservedSlot(INTRODUCTION_SCRIPT_SLOT)
    776                 .isMagic(JS_GENERIC_MAGIC));
    777 
    778  if (!MaybeValidateFilename(cx, source, options)) {
    779    return false;
    780  }
    781 
    782  if (options.deferDebugMetadata) {
    783    return true;
    784  }
    785 
    786  // Initialize the element attribute slot and introduction script slot
    787  // this marks the SSO as initialized for asserts.
    788 
    789  RootedString elementAttributeName(cx);
    790  if (!initElementProperties(cx, source, elementAttributeName)) {
    791    return false;
    792  }
    793 
    794  source->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, UndefinedValue());
    795 
    796  return true;
    797 }
    798 
    799 /* static */
    800 bool ScriptSourceObject::initElementProperties(
    801    JSContext* cx, Handle<ScriptSourceObject*> source,
    802    HandleString elementAttrName) {
    803  RootedValue nameValue(cx);
    804  if (elementAttrName) {
    805    nameValue = StringValue(elementAttrName);
    806  }
    807  if (!cx->compartment()->wrap(cx, &nameValue)) {
    808    return false;
    809  }
    810 
    811  source->setReservedSlot(ELEMENT_PROPERTY_SLOT, nameValue);
    812 
    813  return true;
    814 }
    815 
    816 void ScriptSourceObject::setPrivate(JSRuntime* rt, const Value& value) {
    817  // Update the private value, calling addRef/release hooks if necessary
    818  // to allow the embedding to maintain a reference count for the
    819  // private data.
    820  JS::AutoSuppressGCAnalysis nogc;
    821  Value prevValue = getReservedSlot(PRIVATE_SLOT);
    822  rt->releaseScriptPrivate(prevValue);
    823  setReservedSlot(PRIVATE_SLOT, value);
    824  rt->addRefScriptPrivate(value);
    825 }
    826 
    827 void ScriptSourceObject::clearPrivate(JSRuntime* rt) {
    828  // Clear the private value, calling release hook if necessary.
    829  // |this| may be gray, be careful not to create edges to it.
    830  JS::AutoSuppressGCAnalysis nogc;
    831  Value prevValue = getReservedSlot(PRIVATE_SLOT);
    832  rt->releaseScriptPrivate(prevValue);
    833  getSlotRef(PRIVATE_SLOT).setUndefinedUnchecked();
    834 }
    835 
    836 // Main-thread source loader that can retrieve sources via the source hook.
    837 class ScriptSource::LoadSourceMatcher {
    838  JSContext* const cx_;
    839  ScriptSource* const ss_;
    840  bool* const loaded_;
    841 
    842 public:
    843  explicit LoadSourceMatcher(JSContext* cx, ScriptSource* ss, bool* loaded)
    844      : cx_(cx), ss_(ss), loaded_(loaded) {}
    845 
    846  template <typename Unit, SourceRetrievable CanRetrieve>
    847  bool operator()(const Compressed<Unit, CanRetrieve>&) const {
    848    *loaded_ = true;
    849    return true;
    850  }
    851 
    852  template <typename Unit, SourceRetrievable CanRetrieve>
    853  bool operator()(const Uncompressed<Unit, CanRetrieve>&) const {
    854    *loaded_ = true;
    855    return true;
    856  }
    857 
    858  bool operator()(const Missing&) const {
    859    *loaded_ = false;
    860    return true;
    861  }
    862 
    863  template <typename Unit>
    864  bool operator()(const Retrievable<Unit>&) {
    865    if (!cx_->runtime()->sourceHook.ref()) {
    866      *loaded_ = false;
    867      return true;
    868    }
    869 
    870    size_t length;
    871 
    872    // The first argument is just for overloading -- its value doesn't matter.
    873    if (!tryLoadAndSetSource(Unit('0'), &length)) {
    874      return false;
    875    }
    876 
    877    return true;
    878  }
    879 
    880 private:
    881  bool tryLoadAndSetSource(const Utf8Unit&, size_t* length) const {
    882    char* utf8Source;
    883    if (!cx_->runtime()->sourceHook->load(cx_, ss_->filename(), nullptr,
    884                                          &utf8Source, length)) {
    885      return false;
    886    }
    887 
    888    if (!utf8Source) {
    889      *loaded_ = false;
    890      return true;
    891    }
    892 
    893    if (!ss_->setRetrievedSource(
    894            cx_, EntryUnits<Utf8Unit>(reinterpret_cast<Utf8Unit*>(utf8Source)),
    895            *length)) {
    896      return false;
    897    }
    898 
    899    *loaded_ = true;
    900    return true;
    901  }
    902 
    903  bool tryLoadAndSetSource(const char16_t&, size_t* length) const {
    904    char16_t* utf16Source;
    905    if (!cx_->runtime()->sourceHook->load(cx_, ss_->filename(), &utf16Source,
    906                                          nullptr, length)) {
    907      return false;
    908    }
    909 
    910    if (!utf16Source) {
    911      *loaded_ = false;
    912      return true;
    913    }
    914 
    915    if (!ss_->setRetrievedSource(cx_, EntryUnits<char16_t>(utf16Source),
    916                                 *length)) {
    917      return false;
    918    }
    919 
    920    *loaded_ = true;
    921    return true;
    922  }
    923 };
    924 
    925 /* static */
    926 bool ScriptSource::loadSource(JSContext* cx, ScriptSource* ss, bool* loaded) {
    927  return ss->data.match(LoadSourceMatcher(cx, ss, loaded));
    928 }
    929 
    930 // Matcher to get source properties: whether source is present and whether
    931 // it is retrievable.
    932 class ScriptSource::SourcePropertiesGetter {
    933  bool* const hasSourceText_;
    934  bool* const retrievable_;
    935 
    936 public:
    937  explicit SourcePropertiesGetter(bool* hasSourceText, bool* retrievable)
    938      : hasSourceText_(hasSourceText), retrievable_(retrievable) {}
    939 
    940  template <typename Unit, SourceRetrievable CanRetrieve>
    941  void operator()(const Compressed<Unit, CanRetrieve>&) const {
    942    *hasSourceText_ = true;
    943    *retrievable_ = false;
    944  }
    945 
    946  template <typename Unit, SourceRetrievable CanRetrieve>
    947  void operator()(const Uncompressed<Unit, CanRetrieve>&) const {
    948    *hasSourceText_ = true;
    949    *retrievable_ = false;
    950  }
    951 
    952  template <typename Unit>
    953  void operator()(const Retrievable<Unit>&) {
    954    // Retrievable requires the main thread. Do not attempt to retrieve it.
    955    *hasSourceText_ = false;
    956    *retrievable_ = true;
    957  }
    958 
    959  void operator()(const Missing&) const {
    960    *hasSourceText_ = false;
    961    *retrievable_ = false;
    962  }
    963 };
    964 
    965 void ScriptSource::getSourceProperties(ScriptSource* ss, bool* hasSourceText,
    966                                       bool* retrievable) {
    967  ss->data.match(SourcePropertiesGetter(hasSourceText, retrievable));
    968 }
    969 
    970 /* static */
    971 JSLinearString* JSScript::sourceData(JSContext* cx, HandleScript script) {
    972  MOZ_ASSERT(script->scriptSource()->hasSourceText());
    973  return script->scriptSource()->substring(cx, script->sourceStart(),
    974                                           script->sourceEnd());
    975 }
    976 
    977 bool BaseScript::appendSourceDataForToString(JSContext* cx,
    978                                             StringBuilder& buf) {
    979  MOZ_ASSERT(scriptSource()->hasSourceText());
    980  return scriptSource()->appendSubstring(cx, buf, toStringStart(),
    981                                         toStringEnd());
    982 }
    983 
    984 void UncompressedSourceCache::holdEntry(AutoHoldEntry& holder,
    985                                        const ScriptSourceChunk& ssc) {
    986  MOZ_ASSERT(!holder_);
    987  holder.holdEntry(this, ssc);
    988  holder_ = &holder;
    989 }
    990 
    991 void UncompressedSourceCache::releaseEntry(AutoHoldEntry& holder) {
    992  MOZ_ASSERT(holder_ == &holder);
    993  holder_ = nullptr;
    994 }
    995 
    996 template <typename Unit>
    997 const Unit* UncompressedSourceCache::lookup(const ScriptSourceChunk& ssc,
    998                                            AutoHoldEntry& holder) {
    999  MOZ_ASSERT(!holder_);
   1000  MOZ_ASSERT(ssc.ss->isCompressed<Unit>());
   1001 
   1002  if (!map_) {
   1003    return nullptr;
   1004  }
   1005 
   1006  if (Map::Ptr p = map_->lookup(ssc)) {
   1007    holdEntry(holder, ssc);
   1008    return static_cast<const Unit*>(p->value().get());
   1009  }
   1010 
   1011  return nullptr;
   1012 }
   1013 
   1014 bool UncompressedSourceCache::put(const ScriptSourceChunk& ssc, SourceData data,
   1015                                  AutoHoldEntry& holder) {
   1016  MOZ_ASSERT(!holder_);
   1017 
   1018  if (!map_) {
   1019    map_ = MakeUnique<Map>();
   1020    if (!map_) {
   1021      return false;
   1022    }
   1023  }
   1024 
   1025  if (!map_->put(ssc, std::move(data))) {
   1026    return false;
   1027  }
   1028 
   1029  holdEntry(holder, ssc);
   1030  return true;
   1031 }
   1032 
   1033 void UncompressedSourceCache::purge() {
   1034  if (!map_) {
   1035    return;
   1036  }
   1037 
   1038  for (Map::Range r = map_->all(); !r.empty(); r.popFront()) {
   1039    if (holder_ && r.front().key() == holder_->sourceChunk()) {
   1040      holder_->deferDelete(std::move(r.front().value()));
   1041      holder_ = nullptr;
   1042    }
   1043  }
   1044 
   1045  map_ = nullptr;
   1046 }
   1047 
   1048 size_t UncompressedSourceCache::sizeOfExcludingThis(
   1049    mozilla::MallocSizeOf mallocSizeOf) {
   1050  size_t n = 0;
   1051  if (map_ && !map_->empty()) {
   1052    n += map_->shallowSizeOfIncludingThis(mallocSizeOf);
   1053    for (Map::Range r = map_->all(); !r.empty(); r.popFront()) {
   1054      n += mallocSizeOf(r.front().value().get());
   1055    }
   1056  }
   1057  return n;
   1058 }
   1059 
   1060 template <typename Unit>
   1061 const Unit* ScriptSource::chunkUnits(
   1062    JSContext* maybeCx, UncompressedSourceCache::AutoHoldEntry& holder,
   1063    size_t chunk) {
   1064  const CompressedData<Unit>& c = *compressedData<Unit>();
   1065 
   1066  // Try cache lookup only if we have a JSContext
   1067  if (maybeCx) {
   1068    ScriptSourceChunk ssc(this, chunk);
   1069    if (const Unit* decompressed =
   1070            maybeCx->caches().uncompressedSourceCache.lookup<Unit>(ssc,
   1071                                                                   holder)) {
   1072      return decompressed;
   1073    }
   1074  }
   1075 
   1076  size_t totalLengthInBytes = length() * sizeof(Unit);
   1077  size_t chunkBytes = Compressor::chunkSize(totalLengthInBytes, chunk);
   1078 
   1079  MOZ_ASSERT((chunkBytes % sizeof(Unit)) == 0);
   1080  const size_t chunkLength = chunkBytes / sizeof(Unit);
   1081  EntryUnits<Unit> decompressed(js_pod_malloc<Unit>(chunkLength));
   1082  if (!decompressed) {
   1083    if (maybeCx) {
   1084      JS_ReportOutOfMemory(maybeCx);
   1085    }
   1086    return nullptr;
   1087  }
   1088 
   1089  // Compression treats input and output memory as plain ol' bytes. These
   1090  // reinterpret_cast<>s accord exactly with that.
   1091  if (!DecompressStringChunk(
   1092          reinterpret_cast<const unsigned char*>(c.raw.chars()), chunk,
   1093          reinterpret_cast<unsigned char*>(decompressed.get()), chunkBytes)) {
   1094    if (maybeCx) {
   1095      JS_ReportOutOfMemory(maybeCx);
   1096    }
   1097    return nullptr;
   1098  }
   1099 
   1100  const Unit* ret = decompressed.get();
   1101 
   1102  // Try to cache the result only if we have a JSContext
   1103  if (maybeCx) {
   1104    ScriptSourceChunk ssc(this, chunk);
   1105    if (!maybeCx->caches().uncompressedSourceCache.put(
   1106            ssc, ToSourceData(std::move(decompressed)), holder)) {
   1107      JS_ReportOutOfMemory(maybeCx);
   1108      return nullptr;
   1109    }
   1110  } else {
   1111    // Without caching, transfer ownership to holder for memory management
   1112    holder.holdUnits(std::move(decompressed));
   1113  }
   1114 
   1115  return ret;
   1116 }
   1117 
   1118 template <typename Unit>
   1119 void ScriptSource::convertToCompressedSource(SharedImmutableString compressed,
   1120                                             size_t uncompressedLength) {
   1121  MOZ_ASSERT(isUncompressed<Unit>());
   1122  MOZ_ASSERT(uncompressedData<Unit>()->length() == uncompressedLength);
   1123 
   1124  if (data.is<Uncompressed<Unit, SourceRetrievable::Yes>>()) {
   1125    data = SourceType(Compressed<Unit, SourceRetrievable::Yes>(
   1126        std::move(compressed), uncompressedLength));
   1127  } else {
   1128    data = SourceType(Compressed<Unit, SourceRetrievable::No>(
   1129        std::move(compressed), uncompressedLength));
   1130  }
   1131 }
   1132 
   1133 template <typename Unit>
   1134 void ScriptSource::performDelayedConvertToCompressedSource(
   1135    ExclusiveData<ReaderInstances>::Guard& g) {
   1136  // There might not be a conversion to compressed source happening at all.
   1137  if (g->pendingCompressed.empty()) {
   1138    return;
   1139  }
   1140 
   1141  CompressedData<Unit>& pending =
   1142      g->pendingCompressed.ref<CompressedData<Unit>>();
   1143 
   1144  convertToCompressedSource<Unit>(std::move(pending.raw),
   1145                                  pending.uncompressedLength);
   1146 
   1147  g->pendingCompressed.destroy();
   1148 }
   1149 
   1150 void ScriptSource::PinnedUnitsBase::addReader() {
   1151  auto guard = source_->readers_.lock();
   1152  guard->count++;
   1153 }
   1154 
   1155 template <typename Unit>
   1156 void ScriptSource::PinnedUnitsBase::removeReader() {
   1157  // If the off-thread compression task couldn't perform
   1158  // convertToCompressedSource, the conversion is pending on
   1159  // the pendingCompressed field.
   1160  //
   1161  // If there's no other reader at this point, perform the pending conversion
   1162  // here.
   1163  //
   1164  // See also ScriptSource::triggerConvertToCompressedSource.
   1165  auto guard = source_->readers_.lock();
   1166  MOZ_ASSERT(guard->count > 0);
   1167  if (--guard->count == 0) {
   1168    source_->performDelayedConvertToCompressedSource<Unit>(guard);
   1169  }
   1170 }
   1171 
   1172 template <typename Unit>
   1173 ScriptSource::PinnedUnits<Unit>::~PinnedUnits() {
   1174  if (units_) {
   1175    removeReader<Unit>();
   1176  }
   1177 }
   1178 
   1179 template <typename Unit>
   1180 ScriptSource::PinnedUnitsIfUncompressed<Unit>::~PinnedUnitsIfUncompressed() {
   1181  if (units_) {
   1182    removeReader<Unit>();
   1183  }
   1184 }
   1185 
   1186 template <typename Unit>
   1187 const Unit* ScriptSource::units(JSContext* maybeCx,
   1188                                UncompressedSourceCache::AutoHoldEntry& holder,
   1189                                size_t begin, size_t len) {
   1190  MOZ_ASSERT(begin <= length());
   1191  MOZ_ASSERT(begin + len <= length());
   1192 
   1193  if (isUncompressed<Unit>()) {
   1194    const Unit* units = uncompressedData<Unit>()->units();
   1195    if (!units) {
   1196      return nullptr;
   1197    }
   1198    return units + begin;
   1199  }
   1200 
   1201  if (data.is<Missing>()) {
   1202    MOZ_CRASH("ScriptSource::units() on ScriptSource with missing source");
   1203  }
   1204 
   1205  if (data.is<Retrievable<Unit>>()) {
   1206    MOZ_CRASH("ScriptSource::units() on ScriptSource with retrievable source");
   1207  }
   1208 
   1209  MOZ_ASSERT(isCompressed<Unit>());
   1210 
   1211  // Determine first/last chunks, the offset (in bytes) into the first chunk
   1212  // of the requested units, and the number of bytes in the last chunk.
   1213  //
   1214  // Note that first and last chunk sizes are miscomputed and *must not be
   1215  // used* when the first chunk is the last chunk.
   1216  size_t firstChunk, firstChunkOffset, firstChunkSize;
   1217  size_t lastChunk, lastChunkSize;
   1218  Compressor::rangeToChunkAndOffset(
   1219      begin * sizeof(Unit), (begin + len) * sizeof(Unit), &firstChunk,
   1220      &firstChunkOffset, &firstChunkSize, &lastChunk, &lastChunkSize);
   1221  MOZ_ASSERT(firstChunk <= lastChunk);
   1222  MOZ_ASSERT(firstChunkOffset % sizeof(Unit) == 0);
   1223  MOZ_ASSERT(firstChunkSize % sizeof(Unit) == 0);
   1224 
   1225  size_t firstUnit = firstChunkOffset / sizeof(Unit);
   1226 
   1227  // Directly return units within a single chunk.  UncompressedSourceCache
   1228  // and |holder| will hold the units alive past function return.
   1229  if (firstChunk == lastChunk) {
   1230    const Unit* units = chunkUnits<Unit>(maybeCx, holder, firstChunk);
   1231    if (!units) {
   1232      return nullptr;
   1233    }
   1234 
   1235    return units + firstUnit;
   1236  }
   1237 
   1238  // Otherwise the units span multiple chunks.  Copy successive chunks'
   1239  // decompressed units into freshly-allocated memory to return.
   1240  EntryUnits<Unit> decompressed(js_pod_malloc<Unit>(len));
   1241  if (!decompressed) {
   1242    if (maybeCx) {
   1243      JS_ReportOutOfMemory(maybeCx);
   1244    }
   1245    return nullptr;
   1246  }
   1247 
   1248  Unit* cursor;
   1249 
   1250  {
   1251    // |AutoHoldEntry| is single-shot, and a holder successfully filled in
   1252    // by |chunkUnits| must be destroyed before another can be used.  Thus
   1253    // we can't use |holder| with |chunkUnits| when |chunkUnits| is used
   1254    // with multiple chunks, and we must use and destroy distinct, fresh
   1255    // holders for each chunk.
   1256    UncompressedSourceCache::AutoHoldEntry firstHolder;
   1257    const Unit* units = chunkUnits<Unit>(maybeCx, firstHolder, firstChunk);
   1258    if (!units) {
   1259      return nullptr;
   1260    }
   1261 
   1262    cursor = std::copy_n(units + firstUnit, firstChunkSize / sizeof(Unit),
   1263                         decompressed.get());
   1264  }
   1265 
   1266  for (size_t i = firstChunk + 1; i < lastChunk; i++) {
   1267    UncompressedSourceCache::AutoHoldEntry chunkHolder;
   1268    const Unit* units = chunkUnits<Unit>(maybeCx, chunkHolder, i);
   1269    if (!units) {
   1270      return nullptr;
   1271    }
   1272 
   1273    cursor = std::copy_n(units, Compressor::CHUNK_SIZE / sizeof(Unit), cursor);
   1274  }
   1275 
   1276  {
   1277    UncompressedSourceCache::AutoHoldEntry lastHolder;
   1278    const Unit* units = chunkUnits<Unit>(maybeCx, lastHolder, lastChunk);
   1279    if (!units) {
   1280      return nullptr;
   1281    }
   1282 
   1283    cursor = std::copy_n(units, lastChunkSize / sizeof(Unit), cursor);
   1284  }
   1285 
   1286  MOZ_ASSERT(PointerRangeSize(decompressed.get(), cursor) == len);
   1287 
   1288  // Transfer ownership to |holder|.
   1289  const Unit* ret = decompressed.get();
   1290  holder.holdUnits(std::move(decompressed));
   1291  return ret;
   1292 }
   1293 
   1294 template <typename Unit>
   1295 const Unit* ScriptSource::uncompressedUnits(size_t begin, size_t len) {
   1296  MOZ_ASSERT(begin <= length());
   1297  MOZ_ASSERT(begin + len <= length());
   1298 
   1299  if (!isUncompressed<Unit>()) {
   1300    return nullptr;
   1301  }
   1302 
   1303  const Unit* units = uncompressedData<Unit>()->units();
   1304  if (!units) {
   1305    return nullptr;
   1306  }
   1307  return units + begin;
   1308 }
   1309 
   1310 template <typename Unit>
   1311 ScriptSource::PinnedUnits<Unit>::PinnedUnits(
   1312    JSContext* maybeCx, ScriptSource* source,
   1313    UncompressedSourceCache::AutoHoldEntry& holder, size_t begin, size_t len)
   1314    : PinnedUnitsBase(source) {
   1315  MOZ_ASSERT(source->hasSourceType<Unit>(), "must pin units of source's type");
   1316 
   1317  addReader();
   1318 
   1319  units_ = source->units<Unit>(maybeCx, holder, begin, len);
   1320  if (!units_) {
   1321    removeReader<Unit>();
   1322  }
   1323 }
   1324 
   1325 template class ScriptSource::PinnedUnits<Utf8Unit>;
   1326 template class ScriptSource::PinnedUnits<char16_t>;
   1327 
   1328 template <typename Unit>
   1329 ScriptSource::PinnedUnitsIfUncompressed<Unit>::PinnedUnitsIfUncompressed(
   1330    ScriptSource* source, size_t begin, size_t len)
   1331    : PinnedUnitsBase(source) {
   1332  MOZ_ASSERT(source->hasSourceType<Unit>(), "must pin units of source's type");
   1333 
   1334  addReader();
   1335 
   1336  units_ = source->uncompressedUnits<Unit>(begin, len);
   1337  if (!units_) {
   1338    removeReader<Unit>();
   1339  }
   1340 }
   1341 
   1342 template class ScriptSource::PinnedUnitsIfUncompressed<Utf8Unit>;
   1343 template class ScriptSource::PinnedUnitsIfUncompressed<char16_t>;
   1344 
   1345 JSLinearString* ScriptSource::substring(JSContext* cx, size_t start,
   1346                                        size_t stop) {
   1347  MOZ_ASSERT(start <= stop);
   1348 
   1349  size_t len = stop - start;
   1350  if (!len) {
   1351    return cx->emptyString();
   1352  }
   1353  UncompressedSourceCache::AutoHoldEntry holder;
   1354 
   1355  // UTF-8 source text.
   1356  if (hasSourceType<Utf8Unit>()) {
   1357    PinnedUnits<Utf8Unit> units(cx, this, holder, start, len);
   1358    if (!units.asChars()) {
   1359      return nullptr;
   1360    }
   1361 
   1362    const char* str = units.asChars();
   1363    return NewStringCopyUTF8N(cx, JS::UTF8Chars(str, len));
   1364  }
   1365 
   1366  // UTF-16 source text.
   1367  PinnedUnits<char16_t> units(cx, this, holder, start, len);
   1368  if (!units.asChars()) {
   1369    return nullptr;
   1370  }
   1371 
   1372  return NewStringCopyN<CanGC>(cx, units.asChars(), len);
   1373 }
   1374 
   1375 JSLinearString* ScriptSource::substringDontDeflate(JSContext* cx, size_t start,
   1376                                                   size_t stop) {
   1377  MOZ_ASSERT(start <= stop);
   1378 
   1379  size_t len = stop - start;
   1380  if (!len) {
   1381    return cx->emptyString();
   1382  }
   1383  UncompressedSourceCache::AutoHoldEntry holder;
   1384 
   1385  // UTF-8 source text.
   1386  if (hasSourceType<Utf8Unit>()) {
   1387    PinnedUnits<Utf8Unit> units(cx, this, holder, start, len);
   1388    if (!units.asChars()) {
   1389      return nullptr;
   1390    }
   1391 
   1392    const char* str = units.asChars();
   1393 
   1394    // There doesn't appear to be a non-deflating UTF-8 string creation
   1395    // function -- but then again, it's not entirely clear how current
   1396    // callers benefit from non-deflation.
   1397    return NewStringCopyUTF8N(cx, JS::UTF8Chars(str, len));
   1398  }
   1399 
   1400  // UTF-16 source text.
   1401  PinnedUnits<char16_t> units(cx, this, holder, start, len);
   1402  if (!units.asChars()) {
   1403    return nullptr;
   1404  }
   1405 
   1406  return NewStringCopyNDontDeflate<CanGC>(cx, units.asChars(), len);
   1407 }
   1408 
   1409 SubstringCharsResult ScriptSource::substringChars(size_t start, size_t stop) {
   1410  MOZ_ASSERT(start <= stop);
   1411 
   1412  size_t len = stop - start;
   1413  MOZ_ASSERT(len > 0, "Callers must handle empty sources before calling this");
   1414 
   1415  UncompressedSourceCache::AutoHoldEntry holder;
   1416 
   1417  // UTF-8 source text.
   1418  if (hasSourceType<Utf8Unit>()) {
   1419    // Pass nullptr JSContext - this method is designed to be called
   1420    // off-main-thread where JSContext is not available. Decompression still
   1421    // works but without caching.
   1422    PinnedUnits<Utf8Unit> units(nullptr, this, holder, start, len);
   1423    if (!units.asChars()) {
   1424      // Allocation failure or decompression error.
   1425      return SubstringCharsResult(JS::UniqueChars(nullptr));
   1426    }
   1427 
   1428    const char* str = units.asChars();
   1429    // For UTF-8 source, create a copy of the char data.
   1430    // Note: We allocate exactly `len` bytes without a null terminator.
   1431    // Callers must track the length separately.
   1432    char* copy = static_cast<char*>(js_malloc(len * sizeof(char)));
   1433    if (!copy) {
   1434      // Allocation failure.
   1435      return SubstringCharsResult(JS::UniqueChars(nullptr));
   1436    }
   1437 
   1438    mozilla::PodCopy(copy, str, len);
   1439    return SubstringCharsResult(JS::UniqueChars(copy));
   1440  }
   1441 
   1442  // UTF-16 source text.
   1443  // Pass nullptr JSContext - this method is designed to be called
   1444  // off-main-thread where JSContext is not available. Decompression still works
   1445  // but without caching.
   1446  PinnedUnits<char16_t> units(nullptr, this, holder, start, len);
   1447  if (!units.asChars()) {
   1448    // Allocation failure or decompression error.
   1449    return SubstringCharsResult(JS::UniqueTwoByteChars(nullptr));
   1450  }
   1451 
   1452  // For UTF-16 source, create a copy of the char16_t data.
   1453  // Note: We allocate exactly `len` char16_t elements without a null
   1454  // terminator. Callers must track the length separately.
   1455  char16_t* copy = static_cast<char16_t*>(js_malloc(len * sizeof(char16_t)));
   1456  if (!copy) {
   1457    // Allocation failure.
   1458    return SubstringCharsResult(JS::UniqueTwoByteChars(nullptr));
   1459  }
   1460 
   1461  mozilla::PodCopy(copy, units.asChars(), len);
   1462  return SubstringCharsResult(JS::UniqueTwoByteChars(copy));
   1463 }
   1464 
   1465 bool ScriptSource::appendSubstring(JSContext* cx, StringBuilder& buf,
   1466                                   size_t start, size_t stop) {
   1467  MOZ_ASSERT(start <= stop);
   1468 
   1469  size_t len = stop - start;
   1470  UncompressedSourceCache::AutoHoldEntry holder;
   1471 
   1472  if (hasSourceType<Utf8Unit>()) {
   1473    PinnedUnits<Utf8Unit> pinned(cx, this, holder, start, len);
   1474    if (!pinned.get()) {
   1475      return false;
   1476    }
   1477    if (len > SourceDeflateLimit && !buf.ensureTwoByteChars()) {
   1478      return false;
   1479    }
   1480 
   1481    const Utf8Unit* units = pinned.get();
   1482    return buf.append(units, len);
   1483  } else {
   1484    PinnedUnits<char16_t> pinned(cx, this, holder, start, len);
   1485    if (!pinned.get()) {
   1486      return false;
   1487    }
   1488    if (len > SourceDeflateLimit && !buf.ensureTwoByteChars()) {
   1489      return false;
   1490    }
   1491 
   1492    const char16_t* units = pinned.get();
   1493    return buf.append(units, len);
   1494  }
   1495 }
   1496 
   1497 JSLinearString* ScriptSource::functionBodyString(JSContext* cx) {
   1498  MOZ_ASSERT(isFunctionBody());
   1499 
   1500  size_t start = parameterListEnd_ + FunctionConstructorMedialSigils.length();
   1501  size_t stop = length() - FunctionConstructorFinalBrace.length();
   1502  return substring(cx, start, stop);
   1503 }
   1504 
   1505 SubstringCharsResult ScriptSource::functionBodyStringChars(size_t* outLength) {
   1506  MOZ_ASSERT(isFunctionBody());
   1507  MOZ_ASSERT(outLength);
   1508 
   1509  size_t start = parameterListEnd_ + FunctionConstructorMedialSigils.length();
   1510  size_t stop = length() - FunctionConstructorFinalBrace.length();
   1511  *outLength = stop - start;
   1512 
   1513  // Handle empty function body. Return nullptr to indicate empty result.
   1514  // This is distinct from substringChars which asserts non-empty length.
   1515  if (*outLength == 0) {
   1516    return SubstringCharsResult(JS::UniqueChars(nullptr));
   1517  }
   1518 
   1519  return substringChars(start, stop);
   1520 }
   1521 
   1522 template <typename ContextT, typename Unit>
   1523 [[nodiscard]] bool ScriptSource::setUncompressedSourceHelper(
   1524    ContextT* cx, EntryUnits<Unit>&& source, size_t length,
   1525    SourceRetrievable retrievable) {
   1526  auto& cache = SharedImmutableStringsCache::getSingleton();
   1527 
   1528  auto uniqueChars = SourceTypeTraits<Unit>::toCacheable(std::move(source));
   1529  auto deduped = cache.getOrCreate(std::move(uniqueChars), length);
   1530  if (!deduped) {
   1531    ReportOutOfMemory(cx);
   1532    return false;
   1533  }
   1534 
   1535  if (retrievable == SourceRetrievable::Yes) {
   1536    data = SourceType(
   1537        Uncompressed<Unit, SourceRetrievable::Yes>(std::move(deduped)));
   1538  } else {
   1539    data = SourceType(
   1540        Uncompressed<Unit, SourceRetrievable::No>(std::move(deduped)));
   1541  }
   1542  return true;
   1543 }
   1544 
   1545 template <typename Unit>
   1546 [[nodiscard]] bool ScriptSource::setRetrievedSource(JSContext* cx,
   1547                                                    EntryUnits<Unit>&& source,
   1548                                                    size_t length) {
   1549  MOZ_ASSERT(data.is<Retrievable<Unit>>(),
   1550             "retrieved source can only overwrite the corresponding "
   1551             "retrievable source");
   1552  return setUncompressedSourceHelper(cx, std::move(source), length,
   1553                                     SourceRetrievable::Yes);
   1554 }
   1555 
   1556 bool js::IsOffThreadSourceCompressionEnabled() {
   1557  // If we don't have concurrent execution compression will contend with
   1558  // main-thread execution, in which case we disable. Similarly we don't want to
   1559  // block the thread pool if it is too small.
   1560  return GetHelperThreadCPUCount() > 1 && GetHelperThreadCount() > 1 &&
   1561         CanUseExtraThreads();
   1562 }
   1563 
   1564 bool ScriptSource::tryCompressOffThread(JSContext* cx) {
   1565  // Beware: |js::SynchronouslyCompressSource| assumes that this function is
   1566  // only called once, just after a script has been compiled, and it's never
   1567  // called at some random time after that.  If multiple calls of this can ever
   1568  // occur, that function may require changes.
   1569 
   1570  // The SourceCompressionTask needs to record the major GC number for
   1571  // scheduling.
   1572  MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
   1573 
   1574  // If source compression was already attempted, do not queue a new task.
   1575  if (hadCompressionTask_) {
   1576    return true;
   1577  }
   1578 
   1579  if (!hasUncompressedSource()) {
   1580    // This excludes compressed, missing, and retrievable source.
   1581    return true;
   1582  }
   1583 
   1584  // There are several cases where source compression is not a good idea:
   1585  //  - If the script is tiny, then compression will save little or no space.
   1586  //  - If there is only one core, then compression will contend with JS
   1587  //    execution (which hurts benchmarketing).
   1588  //
   1589  // Otherwise, enqueue a compression task to be processed when a major
   1590  // GC is requested.
   1591 
   1592  if (length() < ScriptSource::MinimumCompressibleLength ||
   1593      !IsOffThreadSourceCompressionEnabled()) {
   1594    return true;
   1595  }
   1596 
   1597  if (!cx->runtime()->addPendingCompressionEntry(this)) {
   1598    ReportOutOfMemory(cx);
   1599    return false;
   1600  }
   1601  return true;
   1602 }
   1603 
   1604 template <typename Unit>
   1605 void ScriptSource::triggerConvertToCompressedSource(
   1606    SharedImmutableString compressed, size_t uncompressedLength) {
   1607  MOZ_ASSERT(isUncompressed<Unit>(),
   1608             "should only be triggering compressed source installation to "
   1609             "overwrite identically-encoded uncompressed source");
   1610  MOZ_ASSERT(uncompressedData<Unit>()->length() == uncompressedLength);
   1611 
   1612  // If units aren't pinned -- and they probably won't be, we'd have to have a
   1613  // GC in the small window of time where a |PinnedUnits| was live -- then we
   1614  // can immediately convert.
   1615  {
   1616    auto guard = readers_.lock();
   1617    if (MOZ_LIKELY(!guard->count)) {
   1618      convertToCompressedSource<Unit>(std::move(compressed),
   1619                                      uncompressedLength);
   1620      return;
   1621    }
   1622 
   1623    // Otherwise, set aside the compressed-data info.  The conversion is
   1624    // performed when the last |PinnedUnits| dies.
   1625    MOZ_ASSERT(guard->pendingCompressed.empty(),
   1626               "shouldn't be multiple conversions happening");
   1627    guard->pendingCompressed.construct<CompressedData<Unit>>(
   1628        std::move(compressed), uncompressedLength);
   1629  }
   1630 }
   1631 
   1632 template <typename Unit>
   1633 [[nodiscard]] bool ScriptSource::initializeWithUnretrievableCompressedSource(
   1634    FrontendContext* fc, UniqueChars&& compressed, size_t rawLength,
   1635    size_t sourceLength) {
   1636  MOZ_ASSERT(data.is<Missing>(), "shouldn't be double-initializing");
   1637  MOZ_ASSERT(compressed != nullptr);
   1638 
   1639  auto& cache = SharedImmutableStringsCache::getSingleton();
   1640  auto deduped = cache.getOrCreate(std::move(compressed), rawLength);
   1641  if (!deduped) {
   1642    ReportOutOfMemory(fc);
   1643    return false;
   1644  }
   1645 
   1646 #ifdef DEBUG
   1647  {
   1648    auto guard = readers_.lock();
   1649    MOZ_ASSERT(
   1650        guard->count == 0,
   1651        "shouldn't be initializing a ScriptSource while its characters "
   1652        "are pinned -- that only makes sense with a ScriptSource actively "
   1653        "being inspected");
   1654  }
   1655 #endif
   1656 
   1657  data = SourceType(Compressed<Unit, SourceRetrievable::No>(std::move(deduped),
   1658                                                            sourceLength));
   1659 
   1660  return true;
   1661 }
   1662 
   1663 template bool ScriptSource::initializeWithUnretrievableCompressedSource<
   1664    Utf8Unit>(FrontendContext* fc, UniqueChars&& compressed, size_t rawLength,
   1665              size_t sourceLength);
   1666 template bool ScriptSource::initializeWithUnretrievableCompressedSource<
   1667    char16_t>(FrontendContext* fc, UniqueChars&& compressed, size_t rawLength,
   1668              size_t sourceLength);
   1669 
   1670 template <typename Unit>
   1671 bool ScriptSource::assignSource(FrontendContext* fc,
   1672                                const ReadOnlyCompileOptions& options,
   1673                                SourceText<Unit>& srcBuf) {
   1674  MOZ_ASSERT(data.is<Missing>(),
   1675             "source assignment should only occur on fresh ScriptSources");
   1676 
   1677  mutedErrors_ = options.mutedErrors();
   1678  delazificationMode_ = options.eagerDelazificationStrategy();
   1679 
   1680  if (options.discardSource) {
   1681    return true;
   1682  }
   1683 
   1684  if (options.sourceIsLazy) {
   1685    data = SourceType(Retrievable<Unit>());
   1686    return true;
   1687  }
   1688 
   1689  auto& cache = SharedImmutableStringsCache::getSingleton();
   1690  auto deduped = cache.getOrCreate(srcBuf.get(), srcBuf.length(), [&srcBuf]() {
   1691    using CharT = typename SourceTypeTraits<Unit>::CharT;
   1692    return srcBuf.ownsUnits()
   1693               ? UniquePtr<CharT[], JS::FreePolicy>(srcBuf.takeChars())
   1694               : DuplicateString(srcBuf.get(), srcBuf.length());
   1695  });
   1696  if (!deduped) {
   1697    ReportOutOfMemory(fc);
   1698    return false;
   1699  }
   1700 
   1701  data =
   1702      SourceType(Uncompressed<Unit, SourceRetrievable::No>(std::move(deduped)));
   1703  return true;
   1704 }
   1705 
   1706 template bool ScriptSource::assignSource(FrontendContext* fc,
   1707                                         const ReadOnlyCompileOptions& options,
   1708                                         SourceText<char16_t>& srcBuf);
   1709 template bool ScriptSource::assignSource(FrontendContext* fc,
   1710                                         const ReadOnlyCompileOptions& options,
   1711                                         SourceText<Utf8Unit>& srcBuf);
   1712 
   1713 [[nodiscard]] static bool reallocUniquePtr(UniqueChars& unique, size_t size) {
   1714  auto newPtr = static_cast<char*>(js_realloc(unique.get(), size));
   1715  if (!newPtr) {
   1716    return false;
   1717  }
   1718 
   1719  // Since the realloc succeeded, unique is now holding a freed pointer.
   1720  (void)unique.release();
   1721  unique.reset(newPtr);
   1722  return true;
   1723 }
   1724 
   1725 template <typename Unit>
   1726 void SourceCompressionTaskEntry::workEncodingSpecific(Compressor& comp) {
   1727  MOZ_ASSERT(source_->isUncompressed<Unit>());
   1728 
   1729  // Try to keep the maximum memory usage down by only allocating half the
   1730  // size of the string, first.
   1731  size_t inputBytes = source_->length() * sizeof(Unit);
   1732  size_t firstSize = inputBytes / 2;
   1733  UniqueChars compressed(js_pod_malloc<char>(firstSize));
   1734  if (!compressed) {
   1735    return;
   1736  }
   1737 
   1738  const Unit* chars = source_->uncompressedData<Unit>()->units();
   1739  if (!comp.setInput(reinterpret_cast<const unsigned char*>(chars),
   1740                     inputBytes)) {
   1741    return;
   1742  }
   1743 
   1744  comp.setOutput(reinterpret_cast<unsigned char*>(compressed.get()), firstSize);
   1745  bool cont = true;
   1746  bool reallocated = false;
   1747  while (cont) {
   1748    if (shouldCancel()) {
   1749      return;
   1750    }
   1751 
   1752    switch (comp.compressMore()) {
   1753      case Compressor::CONTINUE:
   1754        break;
   1755      case Compressor::MOREOUTPUT: {
   1756        if (reallocated) {
   1757          // The compressed string is longer than the original string.
   1758          return;
   1759        }
   1760 
   1761        // The compressed output is greater than half the size of the
   1762        // original string. Reallocate to the full size.
   1763        if (!reallocUniquePtr(compressed, inputBytes)) {
   1764          return;
   1765        }
   1766 
   1767        comp.setOutput(reinterpret_cast<unsigned char*>(compressed.get()),
   1768                       inputBytes);
   1769        reallocated = true;
   1770        break;
   1771      }
   1772      case Compressor::DONE:
   1773        cont = false;
   1774        break;
   1775      case Compressor::OOM:
   1776        return;
   1777    }
   1778  }
   1779 
   1780  size_t totalBytes = comp.totalBytesNeeded();
   1781 
   1782  // Shrink the buffer to the size of the compressed data.
   1783  if (!reallocUniquePtr(compressed, totalBytes)) {
   1784    return;
   1785  }
   1786 
   1787  comp.finish(compressed.get(), totalBytes);
   1788 
   1789  if (shouldCancel()) {
   1790    return;
   1791  }
   1792 
   1793  auto& strings = SharedImmutableStringsCache::getSingleton();
   1794  resultString_ = strings.getOrCreate(std::move(compressed), totalBytes);
   1795 }
   1796 
   1797 PendingSourceCompressionEntry::PendingSourceCompressionEntry(
   1798    JSRuntime* rt, ScriptSource* source)
   1799    : majorGCNumber_(rt->gc.majorGCCount()), source_(source) {
   1800  source->noteSourceCompressionTask();
   1801 }
   1802 
   1803 struct SourceCompressionTaskEntry::PerformTaskWork {
   1804  SourceCompressionTaskEntry* const task_;
   1805  Compressor& comp_;
   1806 
   1807  PerformTaskWork(SourceCompressionTaskEntry* task, Compressor& comp)
   1808      : task_(task), comp_(comp) {}
   1809 
   1810  template <typename Unit, SourceRetrievable CanRetrieve>
   1811  void operator()(const ScriptSource::Uncompressed<Unit, CanRetrieve>&) {
   1812    task_->workEncodingSpecific<Unit>(comp_);
   1813  }
   1814 
   1815  template <typename T>
   1816  void operator()(const T&) {
   1817    MOZ_CRASH(
   1818        "why are we compressing missing, missing-but-retrievable, "
   1819        "or already-compressed source?");
   1820  }
   1821 };
   1822 
   1823 void ScriptSource::performTaskWork(SourceCompressionTaskEntry* task,
   1824                                   Compressor& comp) {
   1825  MOZ_ASSERT(hasUncompressedSource());
   1826  data.match(SourceCompressionTaskEntry::PerformTaskWork(task, comp));
   1827 }
   1828 
   1829 void SourceCompressionTaskEntry::runTask(Compressor& comp) {
   1830  if (shouldCancel()) {
   1831    return;
   1832  }
   1833 
   1834  MOZ_ASSERT(source_->hasUncompressedSource());
   1835 
   1836  source_->performTaskWork(this, comp);
   1837 }
   1838 
   1839 void SourceCompressionTask::runTask() {
   1840  MOZ_ASSERT(!entries_.empty());
   1841  // Note: here and in workEncodingSpecific we abort compression work on OOM
   1842  // since source compression is optional.
   1843  Compressor comp;
   1844  if (!comp.init()) {
   1845    return;
   1846  }
   1847  for (auto& entry : entries_) {
   1848    entry.runTask(comp);
   1849  }
   1850 }
   1851 
   1852 void SourceCompressionTask::runHelperThreadTask(
   1853    AutoLockHelperThreadState& locked) {
   1854  {
   1855    AutoUnlockHelperThreadState unlock(locked);
   1856    this->runTask();
   1857  }
   1858 
   1859  {
   1860    AutoEnterOOMUnsafeRegion oomUnsafe;
   1861    if (!HelperThreadState().compressionFinishedList(locked).append(this)) {
   1862      oomUnsafe.crash("SourceCompressionTask::runHelperThreadTask");
   1863    }
   1864  }
   1865 }
   1866 
   1867 void ScriptSource::triggerConvertToCompressedSourceFromTask(
   1868    SharedImmutableString compressed) {
   1869  data.match(TriggerConvertToCompressedSourceFromTask(this, compressed));
   1870 }
   1871 
   1872 void SourceCompressionTaskEntry::complete() {
   1873  if (!shouldCancel() && resultString_) {
   1874    source_->triggerConvertToCompressedSourceFromTask(std::move(resultString_));
   1875  }
   1876 }
   1877 
   1878 void SourceCompressionTask::complete() {
   1879  MOZ_ASSERT(!entries_.empty());
   1880  for (auto& entry : entries_) {
   1881    entry.complete();
   1882  }
   1883 }
   1884 
   1885 bool js::SynchronouslyCompressSource(JSContext* cx,
   1886                                     JS::Handle<BaseScript*> script) {
   1887  // Finish all pending source compressions, including the single compression
   1888  // task that may have been created (by |ScriptSource::tryCompressOffThread|)
   1889  // just after the script was compiled.  Because we have flushed this queue,
   1890  // no code below needs to synchronize with an off-thread parse task that
   1891  // assumes the immutability of a |ScriptSource|'s data.
   1892  //
   1893  // This *may* end up compressing |script|'s source.  If it does -- we test
   1894  // this below -- that takes care of things.  But if it doesn't, we will
   1895  // synchronously compress ourselves (and as noted above, this won't race
   1896  // anything).
   1897  RunPendingSourceCompressions(cx->runtime());
   1898 
   1899  ScriptSource* ss = script->scriptSource();
   1900 #ifdef DEBUG
   1901  {
   1902    auto guard = ss->readers_.lock();
   1903    MOZ_ASSERT(guard->count == 0,
   1904               "can't synchronously compress while source units are in use");
   1905  }
   1906 #endif
   1907 
   1908  // In principle a previously-triggered compression on a helper thread could
   1909  // have already completed.  If that happens, there's nothing more to do.
   1910  if (ss->hasCompressedSource()) {
   1911    return true;
   1912  }
   1913 
   1914  MOZ_ASSERT(ss->hasUncompressedSource(),
   1915             "shouldn't be compressing uncompressible source");
   1916 
   1917  // Use an explicit scope to delineate the lifetime of |task|, for simplicity.
   1918  {
   1919 #ifdef DEBUG
   1920    uint32_t sourceRefs = ss->refs;
   1921 #endif
   1922    MOZ_ASSERT(sourceRefs > 0, "at least |script| here should have a ref");
   1923 
   1924    // |SourceCompressionTaskEntry::shouldCancel| can periodically result in
   1925    // source compression being canceled if we're not careful. Guarantee that
   1926    // two refs to |ss| are always live in this function (at least one
   1927    // preexisting and one held by the task) so that compression is never
   1928    // canceled.
   1929    auto task = MakeUnique<SourceCompressionTask>(cx->runtime(), ss);
   1930    if (!task) {
   1931      ReportOutOfMemory(cx);
   1932      return false;
   1933    }
   1934 
   1935    MOZ_ASSERT(ss->refs > sourceRefs, "must have at least two refs now");
   1936 
   1937    // Attempt to compress.  This may not succeed if OOM happens, but (because
   1938    // it ordinarily happens on a helper thread) no error will ever be set here.
   1939    MOZ_ASSERT(!cx->isExceptionPending());
   1940    task->runTask();
   1941    MOZ_ASSERT(!cx->isExceptionPending());
   1942 
   1943    // Convert |ss| from uncompressed to compressed data.
   1944    task->complete();
   1945 
   1946    MOZ_ASSERT(!cx->isExceptionPending());
   1947  }
   1948 
   1949  // The only way source won't be compressed here is if OOM happened.
   1950  return ss->hasCompressedSource();
   1951 }
   1952 
   1953 void ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
   1954                                          JS::ScriptSourceInfo* info) const {
   1955  info->misc += mallocSizeOf(this);
   1956  info->numScripts++;
   1957 }
   1958 
   1959 frontend::InitialStencilAndDelazifications*
   1960 ScriptSourceObject::maybeGetStencils() {
   1961  Value stencilsVal = getReservedSlot(STENCILS_SLOT);
   1962  if (stencilsVal.isUndefined()) {
   1963    return nullptr;
   1964  }
   1965 
   1966  return reinterpret_cast<frontend::InitialStencilAndDelazifications*>(
   1967      uintptr_t(stencilsVal.toPrivate()) & ~STENCILS_MASK);
   1968 }
   1969 
   1970 void ScriptSourceObject::clearStencils() {
   1971  auto* stencils = maybeGetStencils();
   1972  if (!stencils) {
   1973    return;
   1974  }
   1975 
   1976  stencils->Release();
   1977  setReservedSlot(STENCILS_SLOT, UndefinedValue());
   1978 }
   1979 
   1980 template <uintptr_t flag>
   1981 void ScriptSourceObject::setStencilsFlag() {
   1982  JS::Value stencilsVal = getReservedSlot(STENCILS_SLOT);
   1983  MOZ_ASSERT(!stencilsVal.isUndefined(),
   1984             "This should be called after setStencils");
   1985  uintptr_t raw = uintptr_t(stencilsVal.toPrivate());
   1986  MOZ_ASSERT((raw & flag) == 0);
   1987  raw |= flag;
   1988  setReservedSlot(STENCILS_SLOT, PrivateValue(raw));
   1989 }
   1990 
   1991 template <uintptr_t flag>
   1992 void ScriptSourceObject::unsetStencilsFlag() {
   1993  JS::Value stencilsVal = getReservedSlot(STENCILS_SLOT);
   1994  MOZ_ASSERT(!stencilsVal.isUndefined(),
   1995             "This should be called after setStencils");
   1996  uintptr_t raw = uintptr_t(stencilsVal.toPrivate());
   1997  raw &= ~flag;
   1998  if (raw & STENCILS_MASK) {
   1999    setReservedSlot(STENCILS_SLOT, PrivateValue(raw));
   2000  } else {
   2001    clearStencils();
   2002  }
   2003 }
   2004 
   2005 template <uintptr_t flag>
   2006 bool ScriptSourceObject::isStencilsFlagSet() const {
   2007  JS::Value stencilsVal = getReservedSlot(STENCILS_SLOT);
   2008  if (stencilsVal.isUndefined()) {
   2009    return false;
   2010  }
   2011  uintptr_t raw = uintptr_t(stencilsVal.toPrivate());
   2012  return bool(raw & flag);
   2013 }
   2014 
   2015 void ScriptSourceObject::setStencils(
   2016    already_AddRefed<frontend::InitialStencilAndDelazifications> stencils) {
   2017  MOZ_ASSERT(!maybeGetStencils());
   2018  setReservedSlot(STENCILS_SLOT, PrivateValue(stencils.take()));
   2019 }
   2020 
   2021 void ScriptSourceObject::setCollectingDelazifications() {
   2022  setStencilsFlag<STENCILS_COLLECTING_DELAZIFICATIONS_FLAG>();
   2023 }
   2024 
   2025 void ScriptSourceObject::unsetCollectingDelazifications() {
   2026  unsetStencilsFlag<STENCILS_COLLECTING_DELAZIFICATIONS_FLAG>();
   2027 }
   2028 
   2029 bool ScriptSourceObject::isCollectingDelazifications() const {
   2030  return isStencilsFlagSet<STENCILS_COLLECTING_DELAZIFICATIONS_FLAG>();
   2031 }
   2032 
   2033 void ScriptSourceObject::setSharingDelazifications() {
   2034  setStencilsFlag<STENCILS_SHARING_DELAZIFICATIONS_FLAG>();
   2035 }
   2036 
   2037 bool ScriptSourceObject::isSharingDelazifications() const {
   2038  return isStencilsFlagSet<STENCILS_SHARING_DELAZIFICATIONS_FLAG>();
   2039 }
   2040 
   2041 template <typename Unit>
   2042 [[nodiscard]] bool ScriptSource::initializeUnretrievableUncompressedSource(
   2043    FrontendContext* fc, EntryUnits<Unit>&& source, size_t length) {
   2044  MOZ_ASSERT(data.is<Missing>(), "must be initializing a fresh ScriptSource");
   2045  return setUncompressedSourceHelper(fc, std::move(source), length,
   2046                                     SourceRetrievable::No);
   2047 }
   2048 
   2049 template bool ScriptSource::initializeUnretrievableUncompressedSource(
   2050    FrontendContext* fc, EntryUnits<Utf8Unit>&& source, size_t length);
   2051 template bool ScriptSource::initializeUnretrievableUncompressedSource(
   2052    FrontendContext* fc, EntryUnits<char16_t>&& source, size_t length);
   2053 
   2054 // Format and return a cx->pod_malloc'ed URL for a generated script like:
   2055 //   {filename} line {lineno} > {introducer}
   2056 // For example:
   2057 //   foo.js line 7 > eval
   2058 // indicating code compiled by the call to 'eval' on line 7 of foo.js.
   2059 UniqueChars js::FormatIntroducedFilename(const char* filename, uint32_t lineno,
   2060                                         const char* introducer) {
   2061  // Compute the length of the string in advance, so we can allocate a
   2062  // buffer of the right size on the first shot.
   2063  //
   2064  // (JS_smprintf would be perfect, as that allocates the result
   2065  // dynamically as it formats the string, but it won't allocate from cx,
   2066  // and wants us to use a special free function.)
   2067  char linenoBuf[15];
   2068  size_t filenameLen = strlen(filename);
   2069  size_t linenoLen = SprintfLiteral(linenoBuf, "%u", lineno);
   2070  size_t introducerLen = strlen(introducer);
   2071  size_t len = filenameLen + 6 /* == strlen(" line ") */ + linenoLen +
   2072               3 /* == strlen(" > ") */ + introducerLen + 1 /* \0 */;
   2073  UniqueChars formatted(js_pod_malloc<char>(len));
   2074  if (!formatted) {
   2075    return nullptr;
   2076  }
   2077 
   2078  mozilla::DebugOnly<size_t> checkLen = snprintf(
   2079      formatted.get(), len, "%s line %s > %s", filename, linenoBuf, introducer);
   2080  MOZ_ASSERT(checkLen == len - 1);
   2081 
   2082  return formatted;
   2083 }
   2084 
   2085 bool ScriptSource::initFromOptions(FrontendContext* fc,
   2086                                   const ReadOnlyCompileOptions& options) {
   2087  MOZ_ASSERT(!filename_);
   2088  MOZ_ASSERT(!introducerFilename_);
   2089 
   2090  mutedErrors_ = options.mutedErrors();
   2091  delazificationMode_ = options.eagerDelazificationStrategy();
   2092 
   2093  startLine_ = options.lineno;
   2094  startColumn_ = JS::LimitedColumnNumberOneOrigin::fromUnlimited(
   2095      JS::ColumnNumberOneOrigin(options.column));
   2096  introductionType_ = options.introductionType;
   2097  setIntroductionOffset(options.introductionOffset);
   2098  // The parameterListEnd_ is initialized later by setParameterListEnd, before
   2099  // we expose any scripts that use this ScriptSource to the debugger.
   2100 
   2101  if (options.hasIntroductionInfo) {
   2102    MOZ_ASSERT(options.introductionType != nullptr);
   2103    const char* filename =
   2104        options.filename() ? options.filename().c_str() : "<unknown>";
   2105    UniqueChars formatted = FormatIntroducedFilename(
   2106        filename, options.introductionLineno, options.introductionType);
   2107    if (!formatted) {
   2108      ReportOutOfMemory(fc);
   2109      return false;
   2110    }
   2111    if (!setFilename(fc, std::move(formatted))) {
   2112      return false;
   2113    }
   2114  } else if (options.filename()) {
   2115    if (!setFilename(fc, options.filename().c_str())) {
   2116      return false;
   2117    }
   2118  }
   2119 
   2120  if (options.introducerFilename()) {
   2121    if (!setIntroducerFilename(fc, options.introducerFilename().c_str())) {
   2122      return false;
   2123    }
   2124  }
   2125 
   2126  return true;
   2127 }
   2128 
   2129 // Use the SharedImmutableString map to deduplicate input string. The input
   2130 // string must be null-terminated.
   2131 template <typename SharedT, typename CharT>
   2132 static SharedT GetOrCreateStringZ(FrontendContext* fc,
   2133                                  UniquePtr<CharT[], JS::FreePolicy>&& str) {
   2134  size_t lengthWithNull = std::char_traits<CharT>::length(str.get()) + 1;
   2135  auto res = SharedImmutableStringsCache::getSingleton().getOrCreate(
   2136      std::move(str), lengthWithNull);
   2137  if (!res) {
   2138    ReportOutOfMemory(fc);
   2139  }
   2140  return res;
   2141 }
   2142 
   2143 SharedImmutableString ScriptSource::getOrCreateStringZ(FrontendContext* fc,
   2144                                                       UniqueChars&& str) {
   2145  return GetOrCreateStringZ<SharedImmutableString>(fc, std::move(str));
   2146 }
   2147 
   2148 SharedImmutableTwoByteString ScriptSource::getOrCreateStringZ(
   2149    FrontendContext* fc, UniqueTwoByteChars&& str) {
   2150  return GetOrCreateStringZ<SharedImmutableTwoByteString>(fc, std::move(str));
   2151 }
   2152 
   2153 bool ScriptSource::setFilename(FrontendContext* fc, const char* filename) {
   2154  UniqueChars owned = DuplicateString(fc, filename);
   2155  if (!owned) {
   2156    return false;
   2157  }
   2158  return setFilename(fc, std::move(owned));
   2159 }
   2160 
   2161 bool ScriptSource::setFilename(FrontendContext* fc, UniqueChars&& filename) {
   2162  MOZ_ASSERT(!filename_);
   2163  filename_ = getOrCreateStringZ(fc, std::move(filename));
   2164  if (filename_) {
   2165    filenameHash_ =
   2166        mozilla::HashStringKnownLength(filename_.chars(), filename_.length());
   2167    return true;
   2168  }
   2169  return false;
   2170 }
   2171 
   2172 bool ScriptSource::setIntroducerFilename(FrontendContext* fc,
   2173                                         const char* filename) {
   2174  UniqueChars owned = DuplicateString(fc, filename);
   2175  if (!owned) {
   2176    return false;
   2177  }
   2178  return setIntroducerFilename(fc, std::move(owned));
   2179 }
   2180 
   2181 bool ScriptSource::setIntroducerFilename(FrontendContext* fc,
   2182                                         UniqueChars&& filename) {
   2183  MOZ_ASSERT(!introducerFilename_);
   2184  introducerFilename_ = getOrCreateStringZ(fc, std::move(filename));
   2185  return bool(introducerFilename_);
   2186 }
   2187 
   2188 bool ScriptSource::setDisplayURL(FrontendContext* fc, const char16_t* url) {
   2189  UniqueTwoByteChars owned = DuplicateString(fc, url);
   2190  if (!owned) {
   2191    return false;
   2192  }
   2193  return setDisplayURL(fc, std::move(owned));
   2194 }
   2195 
   2196 bool ScriptSource::setDisplayURL(FrontendContext* fc,
   2197                                 UniqueTwoByteChars&& url) {
   2198  MOZ_ASSERT(!hasDisplayURL());
   2199  MOZ_ASSERT(url);
   2200  if (url[0] == '\0') {
   2201    return true;
   2202  }
   2203 
   2204  displayURL_ = getOrCreateStringZ(fc, std::move(url));
   2205  return bool(displayURL_);
   2206 }
   2207 
   2208 bool ScriptSource::setSourceMapURL(FrontendContext* fc, const char16_t* url) {
   2209  UniqueTwoByteChars owned = DuplicateString(fc, url);
   2210  if (!owned) {
   2211    return false;
   2212  }
   2213  return setSourceMapURL(fc, std::move(owned));
   2214 }
   2215 
   2216 bool ScriptSource::setSourceMapURL(FrontendContext* fc,
   2217                                   UniqueTwoByteChars&& url) {
   2218  MOZ_ASSERT(url);
   2219  if (url[0] == '\0') {
   2220    return true;
   2221  }
   2222 
   2223  sourceMapURL_ = getOrCreateStringZ(fc, std::move(url));
   2224  return bool(sourceMapURL_);
   2225 }
   2226 
   2227 /* static */ mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent>
   2228    ScriptSource::idCount_;
   2229 
   2230 /*
   2231 * [SMDOC] JSScript data layout (immutable)
   2232 *
   2233 * Script data that shareable across processes. There are no pointers (GC or
   2234 * otherwise) and the data is relocatable.
   2235 *
   2236 * Array elements   Pointed to by         Length
   2237 * --------------   -------------         ------
   2238 * jsbytecode       code()                codeLength()
   2239 * jsscrnote        notes()               noteLength()
   2240 * uint32_t         resumeOffsets()
   2241 * ScopeNote        scopeNotes()
   2242 * TryNote          tryNotes()
   2243 */
   2244 
   2245 /* static */ CheckedInt<uint32_t> ImmutableScriptData::sizeFor(
   2246    uint32_t codeLength, uint32_t noteLength, uint32_t numResumeOffsets,
   2247    uint32_t numScopeNotes, uint32_t numTryNotes) {
   2248  // Take a count of which optional arrays will be used and need offset info.
   2249  unsigned numOptionalArrays = unsigned(numResumeOffsets > 0) +
   2250                               unsigned(numScopeNotes > 0) +
   2251                               unsigned(numTryNotes > 0);
   2252 
   2253  // Compute size including trailing arrays.
   2254  CheckedInt<uint32_t> size = sizeof(ImmutableScriptData);
   2255  size += sizeof(Flags);
   2256  size += CheckedInt<uint32_t>(codeLength) * sizeof(jsbytecode);
   2257  size += CheckedInt<uint32_t>(noteLength) * sizeof(SrcNote);
   2258  size += CheckedInt<uint32_t>(numOptionalArrays) * sizeof(Offset);
   2259  size += CheckedInt<uint32_t>(numResumeOffsets) * sizeof(uint32_t);
   2260  size += CheckedInt<uint32_t>(numScopeNotes) * sizeof(ScopeNote);
   2261  size += CheckedInt<uint32_t>(numTryNotes) * sizeof(TryNote);
   2262 
   2263  return size;
   2264 }
   2265 
   2266 js::UniquePtr<ImmutableScriptData> js::ImmutableScriptData::new_(
   2267    FrontendContext* fc, uint32_t codeLength, uint32_t noteLength,
   2268    uint32_t numResumeOffsets, uint32_t numScopeNotes, uint32_t numTryNotes) {
   2269  auto size = sizeFor(codeLength, noteLength, numResumeOffsets, numScopeNotes,
   2270                      numTryNotes);
   2271  if (!size.isValid()) {
   2272    ReportAllocationOverflow(fc);
   2273    return nullptr;
   2274  }
   2275 
   2276  // Allocate contiguous raw buffer.
   2277  void* raw = fc->getAllocator()->pod_malloc<uint8_t>(size.value());
   2278  MOZ_ASSERT(uintptr_t(raw) % alignof(ImmutableScriptData) == 0);
   2279  if (!raw) {
   2280    return nullptr;
   2281  }
   2282 
   2283  // Constuct the ImmutableScriptData. Trailing arrays are uninitialized but
   2284  // GCPtrs are put into a safe state.
   2285  UniquePtr<ImmutableScriptData> result(new (raw) ImmutableScriptData(
   2286      codeLength, noteLength, numResumeOffsets, numScopeNotes, numTryNotes));
   2287  if (!result) {
   2288    return nullptr;
   2289  }
   2290 
   2291  // Sanity check
   2292  MOZ_ASSERT(result->endOffset() == size.value());
   2293 
   2294  return result;
   2295 }
   2296 
   2297 js::UniquePtr<ImmutableScriptData> js::ImmutableScriptData::new_(
   2298    FrontendContext* fc, uint32_t totalSize) {
   2299  void* raw = fc->getAllocator()->pod_malloc<uint8_t>(totalSize);
   2300  MOZ_ASSERT(uintptr_t(raw) % alignof(ImmutableScriptData) == 0);
   2301  UniquePtr<ImmutableScriptData> result(
   2302      reinterpret_cast<ImmutableScriptData*>(raw));
   2303  return result;
   2304 }
   2305 
   2306 bool js::ImmutableScriptData::validateLayout(uint32_t expectedSize) {
   2307  constexpr size_t HeaderSize = sizeof(js::ImmutableScriptData);
   2308  constexpr size_t OptionalOffsetsMaxSize = 3 * sizeof(Offset);
   2309 
   2310  // Check that the optional-offsets array lies within the allocation before we
   2311  // try to read from it while computing sizes. Remember that the array *ends*
   2312  // at the `optArrayOffset_`.
   2313  static_assert(OptionalOffsetsMaxSize <= HeaderSize);
   2314  if (HeaderSize > optArrayOffset_) {
   2315    return false;
   2316  }
   2317  if (optArrayOffset_ > expectedSize) {
   2318    return false;
   2319  }
   2320 
   2321  // Round-trip the size computation using `CheckedInt` to detect overflow. This
   2322  // should indirectly validate most alignment, size, and ordering requirments.
   2323  auto size = sizeFor(codeLength(), noteLength(), resumeOffsets().size(),
   2324                      scopeNotes().size(), tryNotes().size());
   2325  return size.isValid() && (size.value() == expectedSize);
   2326 }
   2327 
   2328 /* static */
   2329 SharedImmutableScriptData* SharedImmutableScriptData::create(
   2330    FrontendContext* fc) {
   2331  return fc->getAllocator()->new_<SharedImmutableScriptData>();
   2332 }
   2333 
   2334 /* static */
   2335 SharedImmutableScriptData* SharedImmutableScriptData::createWith(
   2336    FrontendContext* fc, js::UniquePtr<ImmutableScriptData>&& isd) {
   2337  MOZ_ASSERT(isd.get());
   2338  SharedImmutableScriptData* sisd = create(fc);
   2339  if (!sisd) {
   2340    return nullptr;
   2341  }
   2342 
   2343  sisd->setOwn(std::move(isd));
   2344  return sisd;
   2345 }
   2346 
   2347 void JSScript::relazify(JSRuntime* rt) {
   2348  js::Scope* scope = enclosingScope();
   2349  UniquePtr<PrivateScriptData> scriptData;
   2350 
   2351  // Any JIT compiles should have been released, so we already point to the
   2352  // interpreter trampoline which supports lazy scripts.
   2353  MOZ_ASSERT_IF(jit::HasJitBackend(), isUsingInterpreterTrampoline(rt));
   2354 
   2355  realm()->removeFromCompileQueue(this);
   2356 
   2357  // Without bytecode, the script counts are invalid so destroy them if they
   2358  // still exist.
   2359  destroyScriptCounts();
   2360 
   2361  // Release the bytecode and gcthings list.
   2362  // NOTE: We clear the PrivateScriptData to nullptr. This is fine because we
   2363  //       only allowed relazification (via AllowRelazify) if the original lazy
   2364  //       script we compiled from had a nullptr PrivateScriptData.
   2365  swapData(scriptData);
   2366  freeSharedData();
   2367 
   2368  // We should not still be in any side-tables for the debugger or
   2369  // code-coverage. The finalizer will not be able to clean them up once
   2370  // bytecode is released. We check in JSFunction::maybeRelazify() for these
   2371  // conditions before requesting relazification.
   2372  MOZ_ASSERT(!coverage::IsLCovEnabled());
   2373  MOZ_ASSERT(!hasScriptCounts());
   2374  MOZ_ASSERT(!hasDebugScript());
   2375 
   2376  // Rollback warmUpData_ to have enclosingScope.
   2377  MOZ_ASSERT(warmUpData_.isWarmUpCount(),
   2378             "JitScript should already be released");
   2379  warmUpData_.resetWarmUpCount(0);
   2380  warmUpData_.initEnclosingScope(scope);
   2381 
   2382  MOZ_ASSERT(isReadyForDelazification());
   2383 }
   2384 
   2385 // Takes ownership of the passed SharedImmutableScriptData and either adds it
   2386 // into the runtime's SharedImmutableScriptDataTable, or frees it if a matching
   2387 // entry already exists and replaces the passed RefPtr with the existing entry.
   2388 /* static */
   2389 bool SharedImmutableScriptData::shareScriptData(
   2390    FrontendContext* fc, RefPtr<SharedImmutableScriptData>& sisd) {
   2391  MOZ_ASSERT(sisd);
   2392  MOZ_ASSERT(sisd->refCount() == 1);
   2393 
   2394  SharedImmutableScriptData* data = sisd.get();
   2395 
   2396  SharedImmutableScriptData::Hasher::Lookup lookup(data);
   2397 
   2398  Maybe<AutoLockGlobalScriptData> lock;
   2399  js::SharedImmutableScriptDataTable& table =
   2400      fc->scriptDataTableHolder()->getMaybeLocked(lock);
   2401 
   2402  SharedImmutableScriptDataTable::AddPtr p = table.lookupForAdd(lookup);
   2403  if (p) {
   2404    MOZ_ASSERT(data != *p);
   2405    sisd = *p;
   2406  } else {
   2407    if (!table.add(p, data)) {
   2408      ReportOutOfMemory(fc);
   2409      return false;
   2410    }
   2411 
   2412    // Being in the table counts as a reference on the script data.
   2413    data->AddRef();
   2414  }
   2415 
   2416  // Refs: sisd argument, SharedImmutableScriptDataTable
   2417  MOZ_ASSERT(sisd->refCount() >= 2);
   2418 
   2419  return true;
   2420 }
   2421 
   2422 static void SweepScriptDataTable(SharedImmutableScriptDataTable& table) {
   2423  // Entries are removed from the table when their reference count is one,
   2424  // i.e. when the only reference to them is from the table entry.
   2425 
   2426  for (SharedImmutableScriptDataTable::Enum e(table); !e.empty();
   2427       e.popFront()) {
   2428    SharedImmutableScriptData* sharedData = e.front();
   2429    if (sharedData->refCount() == 1) {
   2430      sharedData->Release();
   2431      e.removeFront();
   2432    }
   2433  }
   2434 }
   2435 
   2436 void js::SweepScriptData(JSRuntime* rt) {
   2437  SweepScriptDataTable(rt->scriptDataTableHolder().getWithoutLock());
   2438 
   2439  AutoLockGlobalScriptData lock;
   2440  SweepScriptDataTable(js::globalSharedScriptDataTableHolder.get(lock));
   2441 }
   2442 
   2443 inline size_t PrivateScriptData::allocationSize() const { return endOffset(); }
   2444 
   2445 // Initialize and placement-new the trailing arrays.
   2446 PrivateScriptData::PrivateScriptData(uint32_t ngcthings)
   2447    : ngcthings(ngcthings) {
   2448  // Variable-length data begins immediately after PrivateScriptData itself.
   2449  // NOTE: Alignment is computed using cursor/offset so the alignment of
   2450  // PrivateScriptData must be stricter than any trailing array type.
   2451  Offset cursor = sizeof(PrivateScriptData);
   2452 
   2453  // Layout and initialize the gcthings array.
   2454  {
   2455    initElements<JS::GCCellPtr>(cursor, ngcthings);
   2456    cursor += ngcthings * sizeof(JS::GCCellPtr);
   2457  }
   2458 
   2459  // Sanity check.
   2460  MOZ_ASSERT(endOffset() == cursor);
   2461 }
   2462 
   2463 /* static */
   2464 PrivateScriptData* PrivateScriptData::new_(JSContext* cx, uint32_t ngcthings) {
   2465  // Compute size including trailing arrays.
   2466  CheckedInt<Offset> size = sizeof(PrivateScriptData);
   2467  size += CheckedInt<Offset>(ngcthings) * sizeof(JS::GCCellPtr);
   2468  if (!size.isValid()) {
   2469    ReportAllocationOverflow(cx);
   2470    return nullptr;
   2471  }
   2472 
   2473  // Allocate contiguous raw buffer for the trailing arrays.
   2474  void* raw = cx->pod_malloc<uint8_t>(size.value());
   2475  MOZ_ASSERT(uintptr_t(raw) % alignof(PrivateScriptData) == 0);
   2476  if (!raw) {
   2477    return nullptr;
   2478  }
   2479 
   2480  // Constuct the PrivateScriptData. Trailing arrays are uninitialized but
   2481  // GCPtrs are put into a safe state.
   2482  PrivateScriptData* result = new (raw) PrivateScriptData(ngcthings);
   2483  if (!result) {
   2484    return nullptr;
   2485  }
   2486 
   2487  // Sanity check.
   2488  MOZ_ASSERT(result->endOffset() == size.value());
   2489 
   2490  return result;
   2491 }
   2492 
   2493 /* static */
   2494 bool PrivateScriptData::InitFromStencil(
   2495    JSContext* cx, js::HandleScript script,
   2496    const js::frontend::CompilationAtomCache& atomCache,
   2497    const js::frontend::CompilationStencil& stencil,
   2498    js::frontend::CompilationGCOutput& gcOutput,
   2499    const js::frontend::ScriptIndex scriptIndex) {
   2500  js::frontend::ScriptStencil& scriptStencil = stencil.scriptData[scriptIndex];
   2501  uint32_t ngcthings = scriptStencil.gcThingsLength;
   2502 
   2503  MOZ_ASSERT(ngcthings <= INDEX_LIMIT);
   2504 
   2505  // Create and initialize PrivateScriptData
   2506  if (!JSScript::createPrivateScriptData(cx, script, ngcthings)) {
   2507    return false;
   2508  }
   2509 
   2510  js::PrivateScriptData* data = script->data_;
   2511  if (ngcthings) {
   2512    if (!EmitScriptThingsVector(cx, atomCache, stencil, gcOutput,
   2513                                scriptStencil.gcthings(stencil),
   2514                                data->gcthings())) {
   2515      return false;
   2516    }
   2517  }
   2518 
   2519  return true;
   2520 }
   2521 
   2522 void PrivateScriptData::trace(JSTracer* trc) {
   2523  for (JS::GCCellPtr& elem : gcthings()) {
   2524    TraceManuallyBarrieredGCCellPtr(trc, &elem, "script-gcthing");
   2525  }
   2526 }
   2527 
   2528 /*static*/
   2529 JSScript* JSScript::Create(JSContext* cx, JS::Handle<JSFunction*> function,
   2530                           js::Handle<ScriptSourceObject*> sourceObject,
   2531                           const SourceExtent& extent,
   2532                           js::ImmutableScriptFlags flags) {
   2533  return static_cast<JSScript*>(
   2534      BaseScript::New(cx, function, sourceObject, extent, flags));
   2535 }
   2536 
   2537 #ifdef MOZ_VTUNE
   2538 uint32_t JSScript::vtuneMethodID() {
   2539  if (!zone()->scriptVTuneIdMap) {
   2540    auto map = MakeUnique<ScriptVTuneIdMap>();
   2541    if (!map) {
   2542      MOZ_CRASH("Failed to allocate ScriptVTuneIdMap");
   2543    }
   2544 
   2545    zone()->scriptVTuneIdMap = std::move(map);
   2546  }
   2547 
   2548  ScriptVTuneIdMap::AddPtr p = zone()->scriptVTuneIdMap->lookupForAdd(this);
   2549  if (p) {
   2550    return p->value();
   2551  }
   2552 
   2553  MOZ_ASSERT(this->hasBytecode());
   2554 
   2555  uint32_t id = vtune::GenerateUniqueMethodID();
   2556  if (!zone()->scriptVTuneIdMap->add(p, this, id)) {
   2557    MOZ_CRASH("Failed to add vtune method id");
   2558  }
   2559 
   2560  return id;
   2561 }
   2562 #endif
   2563 
   2564 /* static */
   2565 bool JSScript::createPrivateScriptData(JSContext* cx, HandleScript script,
   2566                                       uint32_t ngcthings) {
   2567  cx->check(script);
   2568 
   2569  UniquePtr<PrivateScriptData> data(PrivateScriptData::new_(cx, ngcthings));
   2570  if (!data) {
   2571    return false;
   2572  }
   2573 
   2574  script->swapData(data);
   2575  MOZ_ASSERT(!data);
   2576 
   2577  return true;
   2578 }
   2579 
   2580 /* static */
   2581 bool JSScript::fullyInitFromStencil(
   2582    JSContext* cx, const js::frontend::CompilationAtomCache& atomCache,
   2583    const js::frontend::CompilationStencil& stencil,
   2584    frontend::CompilationGCOutput& gcOutput, HandleScript script,
   2585    const js::frontend::ScriptIndex scriptIndex) {
   2586  MutableScriptFlags lazyMutableFlags;
   2587  Rooted<Scope*> lazyEnclosingScope(cx);
   2588 
   2589  // A holder for the lazy PrivateScriptData that we must keep around in case
   2590  // this process fails and we must return the script to its original state.
   2591  //
   2592  // This is initialized by BaseScript::swapData() which will run pre-barriers
   2593  // for us. On successful conversion to non-lazy script, the old script data
   2594  // here will be released by the UniquePtr.
   2595  Rooted<UniquePtr<PrivateScriptData>> lazyData(cx);
   2596 
   2597  // Whether we are a newborn script or an existing lazy script, we should
   2598  // already be pointing to the interpreter trampoline.
   2599  MOZ_ASSERT_IF(jit::HasJitBackend(),
   2600                script->isUsingInterpreterTrampoline(cx->runtime()));
   2601 
   2602  // If we are using an existing lazy script, record enough info to be able to
   2603  // rollback on failure.
   2604  if (script->isReadyForDelazification()) {
   2605    lazyMutableFlags = script->mutableFlags_;
   2606    lazyEnclosingScope = script->releaseEnclosingScope();
   2607    script->swapData(lazyData.get());
   2608    MOZ_ASSERT(script->sharedData_ == nullptr);
   2609  }
   2610 
   2611  // Restore the script to lazy state on failure. If this was a fresh script, we
   2612  // just need to clear bytecode to mark script as incomplete.
   2613  auto rollbackGuard = mozilla::MakeScopeExit([&] {
   2614    if (lazyEnclosingScope) {
   2615      script->mutableFlags_ = lazyMutableFlags;
   2616      script->warmUpData_.initEnclosingScope(lazyEnclosingScope);
   2617      script->swapData(lazyData.get());
   2618      script->sharedData_ = nullptr;
   2619 
   2620      MOZ_ASSERT(script->isReadyForDelazification());
   2621    } else {
   2622      script->sharedData_ = nullptr;
   2623    }
   2624  });
   2625 
   2626  // The counts of indexed things must be checked during code generation.
   2627  MOZ_ASSERT(stencil.scriptData[scriptIndex].gcThingsLength <= INDEX_LIMIT);
   2628 
   2629  // Note: These flags should already be correct when the BaseScript was
   2630  // allocated.
   2631  MOZ_ASSERT_IF(stencil.isInitialStencil(),
   2632                script->immutableFlags() ==
   2633                    stencil.scriptExtra[scriptIndex].immutableFlags);
   2634 
   2635  // Create and initialize PrivateScriptData
   2636  if (!PrivateScriptData::InitFromStencil(cx, script, atomCache, stencil,
   2637                                          gcOutput, scriptIndex)) {
   2638    return false;
   2639  }
   2640 
   2641  // Member-initializer data is computed in initial parse only. If we are
   2642  // delazifying, make sure to copy it off the `lazyData` before we throw it
   2643  // away.
   2644  if (script->useMemberInitializers()) {
   2645    if (stencil.isInitialStencil()) {
   2646      MemberInitializers initializers(
   2647          stencil.scriptExtra[scriptIndex].memberInitializers());
   2648      script->setMemberInitializers(initializers);
   2649    } else {
   2650      script->setMemberInitializers(lazyData.get()->getMemberInitializers());
   2651    }
   2652  }
   2653  auto* scriptData = stencil.sharedData.get(scriptIndex);
   2654  script->initSharedData(scriptData);
   2655 
   2656  // NOTE: JSScript is now constructed and should be linked in.
   2657  rollbackGuard.release();
   2658 
   2659  // Link Scope -> JSFunction -> BaseScript.
   2660  if (script->isFunction()) {
   2661    JSFunction* fun = gcOutput.getFunction(scriptIndex);
   2662    script->bodyScope()->as<FunctionScope>().initCanonicalFunction(fun);
   2663    if (fun->isIncomplete()) {
   2664      fun->initScript(script);
   2665    } else if (fun->hasSelfHostedLazyScript()) {
   2666      fun->clearSelfHostedLazyScript();
   2667      fun->initScript(script);
   2668    } else {
   2669      // We are delazifying in-place.
   2670      MOZ_ASSERT(fun->baseScript() == script);
   2671    }
   2672  }
   2673 
   2674  // NOTE: The caller is responsible for linking ModuleObjects if this is a
   2675  //       module script.
   2676 
   2677 #ifdef JS_STRUCTURED_SPEW
   2678  // We want this to happen after line number initialization to allow filtering
   2679  // to work.
   2680  script->setSpewEnabled(cx->spewer().enabled(script));
   2681 #endif
   2682 
   2683 #ifdef DEBUG
   2684  script->assertValidJumpTargets();
   2685 #endif
   2686 
   2687  if (coverage::IsLCovEnabled()) {
   2688    if (!coverage::InitScriptCoverage(cx, script)) {
   2689      return false;
   2690    }
   2691  }
   2692 
   2693  return true;
   2694 }
   2695 
   2696 JSScript* JSScript::fromStencil(JSContext* cx,
   2697                                frontend::CompilationAtomCache& atomCache,
   2698                                const frontend::CompilationStencil& stencil,
   2699                                frontend::CompilationGCOutput& gcOutput,
   2700                                frontend::ScriptIndex scriptIndex) {
   2701  js::frontend::ScriptStencil& scriptStencil = stencil.scriptData[scriptIndex];
   2702  js::frontend::ScriptStencilExtra& scriptExtra =
   2703      stencil.scriptExtra[scriptIndex];
   2704  MOZ_ASSERT(scriptStencil.hasSharedData(),
   2705             "Need generated bytecode to use JSScript::fromStencil");
   2706 
   2707  Rooted<JSFunction*> function(cx);
   2708  if (scriptStencil.isFunction()) {
   2709    function = gcOutput.getFunction(scriptIndex);
   2710  }
   2711 
   2712  Rooted<ScriptSourceObject*> sourceObject(cx, gcOutput.sourceObject);
   2713  RootedScript script(cx, Create(cx, function, sourceObject, scriptExtra.extent,
   2714                                 scriptExtra.immutableFlags));
   2715  if (!script) {
   2716    return nullptr;
   2717  }
   2718 
   2719  if (!fullyInitFromStencil(cx, atomCache, stencil, gcOutput, script,
   2720                            scriptIndex)) {
   2721    return nullptr;
   2722  }
   2723 
   2724  return script;
   2725 }
   2726 
   2727 #ifdef DEBUG
   2728 void JSScript::assertValidJumpTargets() const {
   2729  BytecodeLocation mainLoc = mainLocation();
   2730  BytecodeLocation endLoc = endLocation();
   2731  AllBytecodesIterable iter(this);
   2732  for (BytecodeLocation loc : iter) {
   2733    // Check jump instructions' target.
   2734    if (loc.isJump()) {
   2735      BytecodeLocation target = loc.getJumpTarget();
   2736      MOZ_ASSERT(mainLoc <= target && target < endLoc);
   2737      MOZ_ASSERT(target.isJumpTarget());
   2738 
   2739      // All backward jumps must be to a JSOp::LoopHead op. This is an invariant
   2740      // we want to maintain to simplify JIT compilation and bytecode analysis.
   2741      MOZ_ASSERT_IF(target < loc, target.is(JSOp::LoopHead));
   2742      MOZ_ASSERT_IF(target < loc, IsBackedgePC(loc.toRawBytecode()));
   2743 
   2744      // All forward jumps must be to a JSOp::JumpTarget op.
   2745      MOZ_ASSERT_IF(target > loc, target.is(JSOp::JumpTarget));
   2746 
   2747      // Jumps must not cross scope boundaries.
   2748      MOZ_ASSERT(loc.innermostScope(this) == target.innermostScope(this));
   2749 
   2750      // Check fallthrough of conditional jump instructions.
   2751      if (loc.fallsThrough()) {
   2752        BytecodeLocation fallthrough = loc.next();
   2753        MOZ_ASSERT(mainLoc <= fallthrough && fallthrough < endLoc);
   2754        MOZ_ASSERT(fallthrough.isJumpTarget());
   2755      }
   2756    }
   2757 
   2758    // Check table switch case labels.
   2759    if (loc.is(JSOp::TableSwitch)) {
   2760      BytecodeLocation target = loc.getTableSwitchDefaultTarget();
   2761 
   2762      // Default target.
   2763      MOZ_ASSERT(mainLoc <= target && target < endLoc);
   2764      MOZ_ASSERT(target.is(JSOp::JumpTarget));
   2765 
   2766      int32_t low = loc.getTableSwitchLow();
   2767      int32_t high = loc.getTableSwitchHigh();
   2768 
   2769      for (int i = 0; i < high - low + 1; i++) {
   2770        BytecodeLocation switchCase = loc.getTableSwitchCaseTarget(this, i);
   2771        MOZ_ASSERT(mainLoc <= switchCase && switchCase < endLoc);
   2772        MOZ_ASSERT(switchCase.is(JSOp::JumpTarget));
   2773      }
   2774    }
   2775  }
   2776 
   2777  // Check catch/finally blocks as jump targets.
   2778  for (const TryNote& tn : trynotes()) {
   2779    if (tn.kind() != TryNoteKind::Catch && tn.kind() != TryNoteKind::Finally) {
   2780      continue;
   2781    }
   2782 
   2783    jsbytecode* tryStart = offsetToPC(tn.start);
   2784    jsbytecode* tryPc = tryStart - JSOpLength_Try;
   2785    MOZ_ASSERT(JSOp(*tryPc) == JSOp::Try);
   2786 
   2787    jsbytecode* tryTarget = tryStart + tn.length;
   2788    MOZ_ASSERT(main() <= tryTarget && tryTarget < codeEnd());
   2789    MOZ_ASSERT(BytecodeIsJumpTarget(JSOp(*tryTarget)));
   2790  }
   2791 }
   2792 #endif
   2793 
   2794 void JSScript::addSizeOfJitScript(mozilla::MallocSizeOf mallocSizeOf,
   2795                                  size_t* sizeOfJitScript,
   2796                                  size_t* sizeOfAllocSites) const {
   2797  if (!hasJitScript()) {
   2798    return;
   2799  }
   2800 
   2801  jitScript()->addSizeOfIncludingThis(mallocSizeOf, sizeOfJitScript,
   2802                                      sizeOfAllocSites);
   2803 }
   2804 
   2805 js::GlobalObject& JSScript::uninlinedGlobal() const { return global(); }
   2806 
   2807 unsigned js::PCToLineNumber(unsigned startLine,
   2808                            JS::LimitedColumnNumberOneOrigin startCol,
   2809                            SrcNote* notes, SrcNote* notesEnd, jsbytecode* code,
   2810                            jsbytecode* pc,
   2811                            JS::LimitedColumnNumberOneOrigin* columnp) {
   2812  unsigned lineno = startLine;
   2813  JS::LimitedColumnNumberOneOrigin column = startCol;
   2814 
   2815  /*
   2816   * Walk through source notes accumulating their deltas, keeping track of
   2817   * line-number notes, until we pass the note for pc's offset within
   2818   * script->code.
   2819   */
   2820  ptrdiff_t offset = 0;
   2821  ptrdiff_t target = pc - code;
   2822  for (SrcNoteIterator iter(notes, notesEnd); !iter.atEnd(); ++iter) {
   2823    const auto* sn = *iter;
   2824    offset += sn->delta();
   2825    if (offset > target) {
   2826      break;
   2827    }
   2828 
   2829    SrcNoteType type = sn->type();
   2830    if (type == SrcNoteType::SetLine) {
   2831      lineno = SrcNote::SetLine::getLine(sn, startLine);
   2832      column = JS::LimitedColumnNumberOneOrigin();
   2833    } else if (type == SrcNoteType::SetLineColumn) {
   2834      lineno = SrcNote::SetLineColumn::getLine(sn, startLine);
   2835      column = SrcNote::SetLineColumn::getColumn(sn);
   2836    } else if (type == SrcNoteType::NewLine) {
   2837      lineno++;
   2838      column = JS::LimitedColumnNumberOneOrigin();
   2839    } else if (type == SrcNoteType::NewLineColumn) {
   2840      lineno++;
   2841      column = SrcNote::NewLineColumn::getColumn(sn);
   2842    } else if (type == SrcNoteType::ColSpan) {
   2843      column += SrcNote::ColSpan::getSpan(sn);
   2844    }
   2845  }
   2846 
   2847  if (columnp) {
   2848    *columnp = column;
   2849  }
   2850 
   2851  return lineno;
   2852 }
   2853 
   2854 unsigned js::PCToLineNumber(JSScript* script, jsbytecode* pc,
   2855                            JS::LimitedColumnNumberOneOrigin* columnp) {
   2856  /* Cope with InterpreterFrame.pc value prior to entering Interpret. */
   2857  if (!pc) {
   2858    return 0;
   2859  }
   2860 
   2861  return PCToLineNumber(
   2862      script->lineno(), JS::LimitedColumnNumberOneOrigin(script->column()),
   2863      script->notes(), script->notesEnd(), script->code(), pc, columnp);
   2864 }
   2865 
   2866 jsbytecode* js::LineNumberToPC(JSScript* script, unsigned target) {
   2867  ptrdiff_t offset = 0;
   2868  ptrdiff_t best = -1;
   2869  unsigned lineno = script->lineno();
   2870  unsigned bestdiff = SrcNote::MaxOperand;
   2871  for (SrcNoteIterator iter(script->notes(), script->notesEnd()); !iter.atEnd();
   2872       ++iter) {
   2873    const auto* sn = *iter;
   2874    /*
   2875     * Exact-match only if offset is not in the prologue; otherwise use
   2876     * nearest greater-or-equal line number match.
   2877     */
   2878    if (lineno == target && offset >= ptrdiff_t(script->mainOffset())) {
   2879      goto out;
   2880    }
   2881    if (lineno >= target) {
   2882      unsigned diff = lineno - target;
   2883      if (diff < bestdiff) {
   2884        bestdiff = diff;
   2885        best = offset;
   2886      }
   2887    }
   2888    offset += sn->delta();
   2889    SrcNoteType type = sn->type();
   2890    if (type == SrcNoteType::SetLine) {
   2891      lineno = SrcNote::SetLine::getLine(sn, script->lineno());
   2892    } else if (type == SrcNoteType::SetLineColumn) {
   2893      lineno = SrcNote::SetLineColumn::getLine(sn, script->lineno());
   2894    } else if (type == SrcNoteType::NewLine ||
   2895               type == SrcNoteType::NewLineColumn) {
   2896      lineno++;
   2897    }
   2898  }
   2899  if (best >= 0) {
   2900    offset = best;
   2901  }
   2902 out:
   2903  return script->offsetToPC(offset);
   2904 }
   2905 
   2906 JS_PUBLIC_API unsigned js::GetScriptLineExtent(
   2907    JSScript* script, JS::LimitedColumnNumberOneOrigin* columnp) {
   2908  unsigned lineno = script->lineno();
   2909  JS::LimitedColumnNumberOneOrigin column = script->column();
   2910  unsigned maxLineNo = lineno;
   2911  for (SrcNoteIterator iter(script->notes(), script->notesEnd()); !iter.atEnd();
   2912       ++iter) {
   2913    const auto* sn = *iter;
   2914    SrcNoteType type = sn->type();
   2915    if (type == SrcNoteType::SetLine) {
   2916      lineno = SrcNote::SetLine::getLine(sn, script->lineno());
   2917      column = JS::LimitedColumnNumberOneOrigin();
   2918    } else if (type == SrcNoteType::SetLineColumn) {
   2919      lineno = SrcNote::SetLineColumn::getLine(sn, script->lineno());
   2920      column = SrcNote::SetLineColumn::getColumn(sn);
   2921    } else if (type == SrcNoteType::NewLine) {
   2922      lineno++;
   2923      column = JS::LimitedColumnNumberOneOrigin();
   2924    } else if (type == SrcNoteType::NewLineColumn) {
   2925      lineno++;
   2926      column = SrcNote::NewLineColumn::getColumn(sn);
   2927    } else if (type == SrcNoteType::ColSpan) {
   2928      column += SrcNote::ColSpan::getSpan(sn);
   2929    }
   2930 
   2931    if (maxLineNo < lineno) {
   2932      maxLineNo = lineno;
   2933    }
   2934  }
   2935 
   2936  if (columnp) {
   2937    *columnp = column;
   2938  }
   2939 
   2940  return 1 + maxLineNo - script->lineno();
   2941 }
   2942 
   2943 #ifdef JS_CACHEIR_SPEW
   2944 void js::maybeUpdateWarmUpCount(JSScript* script) {
   2945  if (script->needsFinalWarmUpCount()) {
   2946    ScriptFinalWarmUpCountMap* map =
   2947        script->zone()->scriptFinalWarmUpCountMap.get();
   2948    // If needsFinalWarmUpCount is true, ScriptFinalWarmUpCountMap must have
   2949    // already been created and thus must be asserted.
   2950    MOZ_ASSERT(map);
   2951    ScriptFinalWarmUpCountMap::Ptr p = map->lookup(script);
   2952    MOZ_ASSERT(p);
   2953 
   2954    std::get<0>(p->value()) += script->jitScript()->warmUpCount();
   2955  }
   2956 }
   2957 
   2958 void js::maybeSpewScriptFinalWarmUpCount(JSScript* script) {
   2959  if (script->needsFinalWarmUpCount()) {
   2960    ScriptFinalWarmUpCountMap* map =
   2961        script->zone()->scriptFinalWarmUpCountMap.get();
   2962    // If needsFinalWarmUpCount is true, ScriptFinalWarmUpCountMap must have
   2963    // already been created and thus must be asserted.
   2964    MOZ_ASSERT(map);
   2965    ScriptFinalWarmUpCountMap::Ptr p = map->lookup(script);
   2966    MOZ_ASSERT(p);
   2967    auto& tuple = p->value();
   2968    uint32_t warmUpCount = std::get<0>(tuple);
   2969    SharedImmutableString& scriptName = std::get<1>(tuple);
   2970 
   2971    JSContext* cx = TlsContext.get();
   2972    cx->spewer().enableSpewing();
   2973 
   2974    // In the case that we care about a script's final warmup count but the
   2975    // spewer is not enabled, AutoSpewChannel automatically sets and unsets
   2976    // the proper channel for the duration of spewing a health report's warm
   2977    // up count.
   2978    AutoSpewChannel channel(cx, SpewChannel::CacheIRHealthReport, script);
   2979    jit::CacheIRHealth cih;
   2980    cih.spewScriptFinalWarmUpCount(cx, scriptName.chars(), script, warmUpCount);
   2981 
   2982    script->zone()->scriptFinalWarmUpCountMap->remove(script);
   2983    script->setNeedsFinalWarmUpCount(false);
   2984  }
   2985 }
   2986 #endif
   2987 
   2988 void js::DescribeScriptedCallerForDirectEval(JSContext* cx, HandleScript script,
   2989                                             jsbytecode* pc, const char** file,
   2990                                             uint32_t* linenop,
   2991                                             uint32_t* pcOffset,
   2992                                             bool* mutedErrors) {
   2993  MOZ_ASSERT(script->containsPC(pc));
   2994 
   2995  static_assert(JSOpLength_SpreadEval == JSOpLength_StrictSpreadEval,
   2996                "next op after a spread must be at consistent offset");
   2997  static_assert(JSOpLength_Eval == JSOpLength_StrictEval,
   2998                "next op after a direct eval must be at consistent offset");
   2999 
   3000  MOZ_ASSERT(JSOp(*pc) == JSOp::Eval || JSOp(*pc) == JSOp::StrictEval ||
   3001             JSOp(*pc) == JSOp::SpreadEval ||
   3002             JSOp(*pc) == JSOp::StrictSpreadEval);
   3003 
   3004  bool isSpread =
   3005      (JSOp(*pc) == JSOp::SpreadEval || JSOp(*pc) == JSOp::StrictSpreadEval);
   3006  jsbytecode* nextpc =
   3007      pc + (isSpread ? JSOpLength_SpreadEval : JSOpLength_Eval);
   3008  MOZ_ASSERT(JSOp(*nextpc) == JSOp::Lineno);
   3009 
   3010  *file = script->filename();
   3011  *linenop = GET_UINT32(nextpc);
   3012  *pcOffset = script->pcToOffset(pc);
   3013  *mutedErrors = script->mutedErrors();
   3014 }
   3015 
   3016 void js::DescribeScriptedCallerForCompilation(
   3017    JSContext* cx, MutableHandleScript maybeScript, const char** file,
   3018    uint32_t* linenop, uint32_t* pcOffset, bool* mutedErrors) {
   3019  NonBuiltinFrameIter iter(cx, cx->realm()->principals());
   3020 
   3021  if (iter.done()) {
   3022    maybeScript.set(nullptr);
   3023    *file = nullptr;
   3024    *linenop = 0;
   3025    *pcOffset = 0;
   3026    *mutedErrors = false;
   3027    return;
   3028  }
   3029 
   3030  *file = iter.filename();
   3031  *linenop = iter.computeLine();
   3032  *mutedErrors = iter.mutedErrors();
   3033 
   3034  // These values are only used for introducer fields which are debugging
   3035  // information and can be safely left null for wasm frames.
   3036  if (iter.hasScript()) {
   3037    maybeScript.set(iter.script());
   3038    *pcOffset = iter.pc() - maybeScript->code();
   3039  } else {
   3040    maybeScript.set(nullptr);
   3041    *pcOffset = 0;
   3042  }
   3043 }
   3044 
   3045 template <typename SourceSpan, typename TargetSpan>
   3046 void CopySpan(const SourceSpan& source, TargetSpan target) {
   3047  MOZ_ASSERT(source.size() == target.size());
   3048  std::copy(source.cbegin(), source.cend(), target.begin());
   3049 }
   3050 
   3051 /* static */
   3052 js::UniquePtr<ImmutableScriptData> ImmutableScriptData::new_(
   3053    FrontendContext* fc, uint32_t mainOffset, uint32_t nfixed, uint32_t nslots,
   3054    GCThingIndex bodyScopeIndex, uint32_t numICEntries, bool isFunction,
   3055    uint16_t funLength, uint16_t propertyCountEstimate,
   3056    mozilla::Span<const jsbytecode> code, mozilla::Span<const SrcNote> notes,
   3057    mozilla::Span<const uint32_t> resumeOffsets,
   3058    mozilla::Span<const ScopeNote> scopeNotes,
   3059    mozilla::Span<const TryNote> tryNotes) {
   3060  MOZ_RELEASE_ASSERT(code.Length() <= frontend::MaxBytecodeLength);
   3061 
   3062  // There are 1-4 copies of SrcNoteType::Null appended after the source
   3063  // notes. These are a combination of sentinel and padding values.
   3064  static_assert(frontend::MaxSrcNotesLength <= UINT32_MAX - CodeNoteAlign,
   3065                "Length + CodeNoteAlign shouldn't overflow UINT32_MAX");
   3066  size_t noteLength = notes.Length();
   3067  MOZ_RELEASE_ASSERT(noteLength <= frontend::MaxSrcNotesLength);
   3068 
   3069  size_t notePaddingLength = ComputeNotePadding(code.Length(), noteLength);
   3070 
   3071  // Allocate ImmutableScriptData
   3072  js::UniquePtr<ImmutableScriptData> data(ImmutableScriptData::new_(
   3073      fc, code.Length(), noteLength + notePaddingLength, resumeOffsets.Length(),
   3074      scopeNotes.Length(), tryNotes.Length()));
   3075  if (!data) {
   3076    return data;
   3077  }
   3078 
   3079  // Initialize POD fields
   3080  data->mainOffset = mainOffset;
   3081  data->nfixed = nfixed;
   3082  data->nslots = nslots;
   3083  data->bodyScopeIndex = bodyScopeIndex;
   3084  data->numICEntries = numICEntries;
   3085  data->propertyCountEstimate = propertyCountEstimate;
   3086 
   3087  if (isFunction) {
   3088    data->funLength = funLength;
   3089  }
   3090 
   3091  // Initialize trailing arrays
   3092  CopySpan(code, data->codeSpan());
   3093  CopySpan(notes, data->notesSpan().To(noteLength));
   3094  std::fill_n(data->notes() + noteLength, notePaddingLength,
   3095              SrcNote::padding());
   3096  CopySpan(resumeOffsets, data->resumeOffsets());
   3097  CopySpan(scopeNotes, data->scopeNotes());
   3098  CopySpan(tryNotes, data->tryNotes());
   3099 
   3100  return data;
   3101 }
   3102 
   3103 void ScriptWarmUpData::trace(JSTracer* trc) {
   3104  uintptr_t tag = data_ & TagMask;
   3105  switch (tag) {
   3106    case EnclosingScriptTag: {
   3107      BaseScript* enclosingScript = toEnclosingScript();
   3108      BaseScript* prior = enclosingScript;
   3109      TraceManuallyBarrieredEdge(trc, &enclosingScript, "enclosingScript");
   3110      if (enclosingScript != prior) {
   3111        setTaggedPtr<EnclosingScriptTag>(enclosingScript);
   3112      }
   3113      break;
   3114    }
   3115 
   3116    case EnclosingScopeTag: {
   3117      Scope* enclosingScope = toEnclosingScope();
   3118      Scope* prior = enclosingScope;
   3119      TraceManuallyBarrieredEdge(trc, &enclosingScope, "enclosingScope");
   3120      if (enclosingScope != prior) {
   3121        setTaggedPtr<EnclosingScopeTag>(enclosingScope);
   3122      }
   3123      break;
   3124    }
   3125 
   3126    case JitScriptTag: {
   3127      toJitScript()->trace(trc);
   3128      break;
   3129    }
   3130 
   3131    default: {
   3132      MOZ_ASSERT(isWarmUpCount());
   3133      break;
   3134    }
   3135  }
   3136 }
   3137 
   3138 size_t JSScript::calculateLiveFixed(jsbytecode* pc) {
   3139  size_t nlivefixed = numAlwaysLiveFixedSlots();
   3140 
   3141  if (nfixed() != nlivefixed) {
   3142    Scope* scope = lookupScope(pc);
   3143    if (scope) {
   3144      scope = MaybeForwarded(scope);
   3145    }
   3146 
   3147    // Find the nearest LexicalScope in the same script.
   3148    while (scope && scope->is<WithScope>()) {
   3149      scope = scope->enclosing();
   3150      if (scope) {
   3151        scope = MaybeForwarded(scope);
   3152      }
   3153    }
   3154 
   3155    if (scope) {
   3156      if (scope->is<LexicalScope>()) {
   3157        nlivefixed = scope->as<LexicalScope>().nextFrameSlot();
   3158      } else if (scope->is<VarScope>()) {
   3159        nlivefixed = scope->as<VarScope>().nextFrameSlot();
   3160      } else if (scope->is<ClassBodyScope>()) {
   3161        nlivefixed = scope->as<ClassBodyScope>().nextFrameSlot();
   3162      }
   3163    }
   3164  }
   3165 
   3166  MOZ_ASSERT(nlivefixed <= nfixed());
   3167  MOZ_ASSERT(nlivefixed >= numAlwaysLiveFixedSlots());
   3168 
   3169  return nlivefixed;
   3170 }
   3171 
   3172 Scope* JSScript::lookupScope(const jsbytecode* pc) const {
   3173  MOZ_ASSERT(containsPC(pc));
   3174 
   3175  size_t offset = pc - code();
   3176 
   3177  auto notes = scopeNotes();
   3178  Scope* scope = nullptr;
   3179 
   3180  // Find the innermost block chain using a binary search.
   3181  size_t bottom = 0;
   3182  size_t top = notes.size();
   3183 
   3184  while (bottom < top) {
   3185    size_t mid = bottom + (top - bottom) / 2;
   3186    const ScopeNote* note = &notes[mid];
   3187    if (note->start <= offset) {
   3188      // Block scopes are ordered in the list by their starting offset, and
   3189      // since blocks form a tree ones earlier in the list may cover the pc even
   3190      // if later blocks end before the pc. This only happens when the earlier
   3191      // block is a parent of the later block, so we need to check parents of
   3192      // |mid| in the searched range for coverage.
   3193      size_t check = mid;
   3194      while (check >= bottom) {
   3195        const ScopeNote* checkNote = &notes[check];
   3196        MOZ_ASSERT(checkNote->start <= offset);
   3197        if (offset < checkNote->start + checkNote->length) {
   3198          // We found a matching block chain but there may be inner ones
   3199          // at a higher block chain index than mid. Continue the binary search.
   3200          if (checkNote->index == ScopeNote::NoScopeIndex) {
   3201            scope = nullptr;
   3202          } else {
   3203            scope = getScope(checkNote->index);
   3204          }
   3205          break;
   3206        }
   3207        if (checkNote->parent == UINT32_MAX) {
   3208          break;
   3209        }
   3210        check = checkNote->parent;
   3211      }
   3212      bottom = mid + 1;
   3213    } else {
   3214      top = mid;
   3215    }
   3216  }
   3217 
   3218  return scope;
   3219 }
   3220 
   3221 Scope* JSScript::innermostScope(const jsbytecode* pc) const {
   3222  if (Scope* scope = lookupScope(pc)) {
   3223    return scope;
   3224  }
   3225  return bodyScope();
   3226 }
   3227 
   3228 void js::SetFrameArgumentsObject(JSContext* cx, AbstractFramePtr frame,
   3229                                 JSObject* argsobj) {
   3230  /*
   3231   * If the arguments object was optimized out by scalar replacement,
   3232   * we must recreate it when we bail out. Because 'arguments' may have
   3233   * already been overwritten, we must check to see if the slot already
   3234   * contains a value.
   3235   */
   3236 
   3237  JSScript* script = frame.script();
   3238 
   3239  BindingIter bi(script);
   3240  while (bi && bi.name() != cx->names().arguments) {
   3241    bi++;
   3242  }
   3243  if (!bi) {
   3244    return;
   3245  }
   3246 
   3247  if (bi.location().kind() == BindingLocation::Kind::Environment) {
   3248 #ifdef DEBUG
   3249    /*
   3250     * If |arguments| lives in the call object, we should not have
   3251     * optimized it. Scan the script to find the slot in the call
   3252     * object that |arguments| is assigned to and verify that it
   3253     * already exists.
   3254     */
   3255    jsbytecode* pc = script->code();
   3256    while (JSOp(*pc) != JSOp::Arguments) {
   3257      pc += GetBytecodeLength(pc);
   3258    }
   3259    pc += JSOpLength_Arguments;
   3260    MOZ_ASSERT(JSOp(*pc) == JSOp::SetAliasedVar);
   3261 
   3262    EnvironmentObject& env = frame.callObj().as<EnvironmentObject>();
   3263    MOZ_ASSERT(!env.aliasedBinding(bi).isMagic(JS_OPTIMIZED_OUT));
   3264 #endif
   3265    return;
   3266  }
   3267 
   3268  MOZ_ASSERT(bi.location().kind() == BindingLocation::Kind::Frame);
   3269  uint32_t frameSlot = bi.location().slot();
   3270  if (frame.unaliasedLocal(frameSlot).isMagic(JS_OPTIMIZED_OUT)) {
   3271    frame.unaliasedLocal(frameSlot) = ObjectValue(*argsobj);
   3272  }
   3273 }
   3274 
   3275 bool JSScript::formalIsAliased(unsigned argSlot) {
   3276  if (functionHasParameterExprs()) {
   3277    return false;
   3278  }
   3279 
   3280  for (PositionalFormalParameterIter fi(this); fi; fi++) {
   3281    if (fi.argumentSlot() == argSlot) {
   3282      return fi.closedOver();
   3283    }
   3284  }
   3285  MOZ_CRASH("Argument slot not found");
   3286 }
   3287 
   3288 // Returns true if any formal argument is mapped by the arguments
   3289 // object, but lives in the call object.
   3290 bool JSScript::anyFormalIsForwarded() {
   3291  if (!argsObjAliasesFormals()) {
   3292    return false;
   3293  }
   3294 
   3295  for (PositionalFormalParameterIter fi(this); fi; fi++) {
   3296    if (fi.closedOver()) {
   3297      return true;
   3298    }
   3299  }
   3300  return false;
   3301 }
   3302 
   3303 bool JSScript::formalLivesInArgumentsObject(unsigned argSlot) {
   3304  return argsObjAliasesFormals() && !formalIsAliased(argSlot);
   3305 }
   3306 
   3307 BaseScript::BaseScript(uint8_t* stubEntry, JSFunction* function,
   3308                       ScriptSourceObject* sourceObject,
   3309                       const SourceExtent& extent, uint32_t immutableFlags)
   3310    : TenuredCellWithNonGCPointer(stubEntry),
   3311      function_(function),
   3312      sourceObject_(sourceObject),
   3313      extent_(extent),
   3314      immutableFlags_(immutableFlags) {
   3315  MOZ_ASSERT(extent_.toStringStart <= extent_.sourceStart);
   3316  MOZ_ASSERT(extent_.sourceStart <= extent_.sourceEnd);
   3317  MOZ_ASSERT(extent_.sourceEnd <= extent_.toStringEnd);
   3318 }
   3319 
   3320 /* static */
   3321 BaseScript* BaseScript::New(JSContext* cx, JS::Handle<JSFunction*> function,
   3322                            Handle<ScriptSourceObject*> sourceObject,
   3323                            const SourceExtent& extent,
   3324                            uint32_t immutableFlags) {
   3325  uint8_t* stubEntry = nullptr;
   3326  if (jit::HasJitBackend()) {
   3327    stubEntry = cx->runtime()->jitRuntime()->interpreterStub().value;
   3328  }
   3329 
   3330  MOZ_ASSERT_IF(function,
   3331                function->compartment() == sourceObject->compartment());
   3332  MOZ_ASSERT_IF(function, function->realm() == sourceObject->realm());
   3333 
   3334  return cx->newCell<BaseScript>(stubEntry, function, sourceObject, extent,
   3335                                 immutableFlags);
   3336 }
   3337 
   3338 /* static */
   3339 BaseScript* BaseScript::CreateRawLazy(JSContext* cx, uint32_t ngcthings,
   3340                                      HandleFunction fun,
   3341                                      Handle<ScriptSourceObject*> sourceObject,
   3342                                      const SourceExtent& extent,
   3343                                      uint32_t immutableFlags) {
   3344  cx->check(fun);
   3345 
   3346  BaseScript* lazy = New(cx, fun, sourceObject, extent, immutableFlags);
   3347  if (!lazy) {
   3348    return nullptr;
   3349  }
   3350 
   3351  // Allocate a PrivateScriptData if it will not be empty. Lazy class
   3352  // constructors that use member initializers also need PrivateScriptData for
   3353  // field data.
   3354  //
   3355  // This condition is implicit in BaseScript::hasPrivateScriptData, and should
   3356  // be mirrored on InputScript::hasPrivateScriptData.
   3357  if (ngcthings || lazy->useMemberInitializers()) {
   3358    UniquePtr<PrivateScriptData> data(PrivateScriptData::new_(cx, ngcthings));
   3359    if (!data) {
   3360      return nullptr;
   3361    }
   3362    lazy->swapData(data);
   3363    MOZ_ASSERT(!data);
   3364  }
   3365 
   3366  return lazy;
   3367 }
   3368 
   3369 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
   3370 // This is an arbitrary non-null pointer that we use as a placeholder
   3371 // for scripts that can be run in PBL: the rest of the engine expects
   3372 // a "non-null jitcode pointer" but we'll never actually call it. We
   3373 // have to ensure alignment to keep GC happy.
   3374 static uint8_t* const PBLJitCodePtr = reinterpret_cast<uint8_t*>(8);
   3375 #endif
   3376 
   3377 void JSScript::updateJitCodeRaw(JSRuntime* rt) {
   3378  MOZ_ASSERT(rt);
   3379  if (hasBaselineScript() && baselineScript()->hasPendingIonCompileTask()) {
   3380    MOZ_ASSERT(!isIonCompilingOffThread());
   3381    setJitCodeRaw(rt->jitRuntime()->lazyLinkStub().value);
   3382  } else if (hasIonScript()) {
   3383    jit::IonScript* ion = ionScript();
   3384    setJitCodeRaw(ion->method()->raw());
   3385  } else if (hasBaselineScript()) {
   3386    setJitCodeRaw(baselineScript()->method()->raw());
   3387  } else if (hasJitScript() && js::jit::IsBaselineInterpreterEnabled()) {
   3388    bool usingEntryTrampoline = false;
   3389    if (js::jit::JitOptions.emitInterpreterEntryTrampoline) {
   3390      auto p = rt->jitRuntime()->getInterpreterEntryMap()->lookup(this);
   3391      if (p) {
   3392        setJitCodeRaw(p->value().raw());
   3393        usingEntryTrampoline = true;
   3394      }
   3395    }
   3396    if (!usingEntryTrampoline) {
   3397      setJitCodeRaw(rt->jitRuntime()->baselineInterpreter().codeRaw());
   3398    }
   3399 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
   3400  } else if (hasJitScript() &&
   3401             js::jit::IsPortableBaselineInterpreterEnabled()) {
   3402    // The portable baseline interpreter does not dispatch on this
   3403    // pointer, but it needs to be non-null to trigger the appropriate
   3404    // code-paths, so we set it to a placeholder value here.
   3405    setJitCodeRaw(PBLJitCodePtr);
   3406 #endif  // ENABLE_PORTABLE_BASELINE_INTERP
   3407  } else if (!js::jit::IsBaselineInterpreterEnabled()) {
   3408    setJitCodeRaw(nullptr);
   3409  } else {
   3410    setJitCodeRaw(rt->jitRuntime()->interpreterStub().value);
   3411  }
   3412  MOZ_ASSERT_IF(!js::jit::IsPortableBaselineInterpreterEnabled(), jitCodeRaw());
   3413 }
   3414 
   3415 bool JSScript::hasLoops() {
   3416  for (const TryNote& tn : trynotes()) {
   3417    if (tn.isLoop()) {
   3418      return true;
   3419    }
   3420  }
   3421  return false;
   3422 }
   3423 
   3424 bool JSScript::mayReadFrameArgsDirectly() {
   3425  return needsArgsObj() || usesArgumentsIntrinsics() || hasRest();
   3426 }
   3427 
   3428 void JSScript::resetWarmUpCounterToDelayIonCompilation() {
   3429  // Reset the warm-up count only if it's greater than the BaselineCompiler
   3430  // threshold. We do this to ensure this has no effect on Baseline compilation
   3431  // because we don't want scripts to get stuck in the (Baseline) interpreter in
   3432  // pathological cases.
   3433 
   3434  if (getWarmUpCount() > jit::JitOptions.baselineJitWarmUpThreshold) {
   3435    incWarmUpResetCounter();
   3436    uint32_t newCount = jit::JitOptions.baselineJitWarmUpThreshold;
   3437    if (warmUpData_.isWarmUpCount()) {
   3438      warmUpData_.resetWarmUpCount(newCount);
   3439    } else {
   3440      warmUpData_.toJitScript()->resetWarmUpCount(newCount);
   3441    }
   3442  }
   3443 }
   3444 
   3445 #if defined(DEBUG) || defined(JS_JITSPEW)
   3446 
   3447 void BaseScript::dumpStringContent(js::GenericPrinter& out) const {
   3448  StringEscape esc('"');
   3449  EscapePrinter ep(out, esc);
   3450  ep.printf("%s:%u:%u @ 0x%p", filename() ? filename() : "<null>", lineno(),
   3451            column().oneOriginValue(), this);
   3452 }
   3453 
   3454 void JSScript::dump(JSContext* cx) {
   3455  JS::Rooted<JSScript*> script(cx, this);
   3456 
   3457  js::Sprinter sp(cx);
   3458  if (!sp.init()) {
   3459    return;
   3460  }
   3461 
   3462  DumpOptions options;
   3463  options.runtimeData = true;
   3464  if (!dump(cx, script, options, &sp)) {
   3465    return;
   3466  }
   3467 
   3468  JS::UniqueChars str = sp.release();
   3469  if (!str) {
   3470    return;
   3471  }
   3472  fprintf(stderr, "%s\n", str.get());
   3473 }
   3474 
   3475 void JSScript::dumpRecursive(JSContext* cx) {
   3476  JS::Rooted<JSScript*> script(cx, this);
   3477 
   3478  js::Sprinter sp(cx);
   3479  if (!sp.init()) {
   3480    return;
   3481  }
   3482 
   3483  DumpOptions options;
   3484  options.runtimeData = true;
   3485  options.recursive = true;
   3486  if (!dump(cx, script, options, &sp)) {
   3487    return;
   3488  }
   3489 
   3490  JS::UniqueChars str = sp.release();
   3491  if (!str) {
   3492    return;
   3493  }
   3494  fprintf(stderr, "%s\n", str.get());
   3495 }
   3496 
   3497 static void DumpMutableScriptFlags(js::JSONPrinter& json,
   3498                                   MutableScriptFlags mutableFlags) {
   3499  // Skip warmup data.
   3500  static_assert(int(MutableScriptFlagsEnum::WarmupResets_MASK) == 0xff);
   3501 
   3502  for (uint32_t i = 0x100; i; i = i << 1) {
   3503    if (uint32_t(mutableFlags) & i) {
   3504      switch (MutableScriptFlagsEnum(i)) {
   3505        case MutableScriptFlagsEnum::HasRunOnce:
   3506          json.value("HasRunOnce");
   3507          break;
   3508        case MutableScriptFlagsEnum::HasBeenCloned:
   3509          json.value("HasBeenCloned");
   3510          break;
   3511        case MutableScriptFlagsEnum::HasScriptCounts:
   3512          json.value("HasScriptCounts");
   3513          break;
   3514        case MutableScriptFlagsEnum::HasDebugScript:
   3515          json.value("HasDebugScript");
   3516          break;
   3517        case MutableScriptFlagsEnum::AllowRelazify:
   3518          json.value("AllowRelazify");
   3519          break;
   3520        case MutableScriptFlagsEnum::SpewEnabled:
   3521          json.value("SpewEnabled");
   3522          break;
   3523        case MutableScriptFlagsEnum::NeedsFinalWarmUpCount:
   3524          json.value("NeedsFinalWarmUpCount");
   3525          break;
   3526        case MutableScriptFlagsEnum::BaselineDisabled:
   3527          json.value("BaselineDisabled");
   3528          break;
   3529        case MutableScriptFlagsEnum::IonDisabled:
   3530          json.value("IonDisabled");
   3531          break;
   3532        case MutableScriptFlagsEnum::Uninlineable:
   3533          json.value("Uninlineable");
   3534          break;
   3535        case MutableScriptFlagsEnum::NoEagerBaselineHint:
   3536          json.value("NoEagerBaselineHint");
   3537          break;
   3538        case MutableScriptFlagsEnum::FailedBoundsCheck:
   3539          json.value("FailedBoundsCheck");
   3540          break;
   3541        case MutableScriptFlagsEnum::HadLICMInvalidation:
   3542          json.value("HadLICMInvalidation");
   3543          break;
   3544        case MutableScriptFlagsEnum::HadReorderingBailout:
   3545          json.value("HadReorderingBailout");
   3546          break;
   3547        case MutableScriptFlagsEnum::HadEagerTruncationBailout:
   3548          json.value("HadEagerTruncationBailout");
   3549          break;
   3550        case MutableScriptFlagsEnum::FailedLexicalCheck:
   3551          json.value("FailedLexicalCheck");
   3552          break;
   3553        case MutableScriptFlagsEnum::HadSpeculativePhiBailout:
   3554          json.value("HadSpeculativePhiBailout");
   3555          break;
   3556        case MutableScriptFlagsEnum::HadUnboxFoldingBailout:
   3557          json.value("HadUnboxFoldingBailout");
   3558          break;
   3559        default:
   3560          json.value("Unknown(%x)", i);
   3561          break;
   3562      }
   3563    }
   3564  }
   3565 }
   3566 
   3567 /* static */
   3568 bool JSScript::dump(JSContext* cx, JS::Handle<JSScript*> script,
   3569                    DumpOptions& options, js::StringPrinter* sp) {
   3570  {
   3571    JSONPrinter json(*sp);
   3572 
   3573    json.beginObject();
   3574 
   3575    if (const char* filename = script->filename()) {
   3576      json.property("file", filename);
   3577    } else {
   3578      json.nullProperty("file");
   3579    }
   3580 
   3581    json.property("lineno", script->lineno());
   3582    json.property("column", script->column().oneOriginValue());
   3583 
   3584    json.beginListProperty("immutableFlags");
   3585    DumpImmutableScriptFlags(json, script->immutableFlags());
   3586    json.endList();
   3587 
   3588    if (options.runtimeData) {
   3589      json.beginListProperty("mutableFlags");
   3590      DumpMutableScriptFlags(json, script->mutableFlags_);
   3591      json.endList();
   3592    }
   3593 
   3594    if (script->isFunction()) {
   3595      JS::Rooted<JSFunction*> fun(cx, script->function());
   3596 
   3597      JS::Rooted<JSAtom*> name(cx, fun->fullDisplayAtom());
   3598      if (name) {
   3599        UniqueChars bytes = JS_EncodeStringToUTF8(cx, name);
   3600        if (!bytes) {
   3601          return false;
   3602        }
   3603        json.property("functionName", bytes.get());
   3604      } else {
   3605        json.nullProperty("functionName");
   3606      }
   3607 
   3608      json.beginListProperty("functionFlags");
   3609      DumpFunctionFlagsItems(json, fun->flags());
   3610      json.endList();
   3611    }
   3612 
   3613    json.endObject();
   3614  }
   3615 
   3616  if (sp->hadOutOfMemory()) {
   3617    sp->forwardOutOfMemory();
   3618    return false;
   3619  }
   3620 
   3621  sp->put("\n");
   3622 
   3623  if (!Disassemble(cx, script, /* lines = */ true, sp)) {
   3624    return false;
   3625  }
   3626  if (!dumpSrcNotes(cx, script, sp)) {
   3627    return false;
   3628  }
   3629  if (!dumpTryNotes(cx, script, sp)) {
   3630    return false;
   3631  }
   3632  if (!dumpScopeNotes(cx, script, sp)) {
   3633    return false;
   3634  }
   3635  if (!dumpGCThings(cx, script, sp)) {
   3636    return false;
   3637  }
   3638 
   3639  if (options.recursive) {
   3640    for (JS::GCCellPtr gcThing : script->gcthings()) {
   3641      if (!gcThing.is<JSObject>()) {
   3642        continue;
   3643      }
   3644 
   3645      JSObject* obj = &gcThing.as<JSObject>();
   3646      if (obj->is<JSFunction>()) {
   3647        sp->put("\n");
   3648 
   3649        JS::Rooted<JSFunction*> fun(cx, &obj->as<JSFunction>());
   3650        if (fun->isInterpreted()) {
   3651          JS::Rooted<JSScript*> innerScript(
   3652              cx, JSFunction::getOrCreateScript(cx, fun));
   3653          if (!innerScript) {
   3654            return false;
   3655          }
   3656          if (!dump(cx, innerScript, options, sp)) {
   3657            return false;
   3658          }
   3659        } else {
   3660          sp->put("[native code]\n");
   3661        }
   3662      }
   3663    }
   3664  }
   3665 
   3666  return true;
   3667 }
   3668 
   3669 /* static */
   3670 bool JSScript::dumpSrcNotes(JSContext* cx, JS::Handle<JSScript*> script,
   3671                            js::GenericPrinter* sp) {
   3672  sp->put("\nSource notes:\n");
   3673  sp->printf("%4s %4s %6s %5s %6s %-16s %s\n", "ofs", "line", "column", "pc",
   3674             "delta", "desc", "args");
   3675  sp->put("---- ---- ------ ----- ------ ---------------- ------\n");
   3676  unsigned offset = 0;
   3677  unsigned lineno = script->lineno();
   3678  JS::LimitedColumnNumberOneOrigin column = script->column();
   3679  SrcNote* notes = script->notes();
   3680  SrcNote* notesEnd = script->notesEnd();
   3681  for (SrcNoteIterator iter(notes, notesEnd); !iter.atEnd(); ++iter) {
   3682    const auto* sn = *iter;
   3683 
   3684    unsigned delta = sn->delta();
   3685    offset += delta;
   3686    SrcNoteType type = sn->type();
   3687    const char* name = sn->name();
   3688    sp->printf("%3u: %4u %6u %5u [%4u] %-16s", unsigned(sn - notes), lineno,
   3689               column.oneOriginValue(), offset, delta, name);
   3690 
   3691    switch (type) {
   3692      case SrcNoteType::Breakpoint:
   3693      case SrcNoteType::BreakpointStepSep:
   3694      case SrcNoteType::XDelta:
   3695        break;
   3696 
   3697      case SrcNoteType::ColSpan: {
   3698        JS::ColumnNumberOffset colspan = SrcNote::ColSpan::getSpan(sn);
   3699        sp->printf(" colspan %u", colspan.value());
   3700        column += colspan;
   3701        break;
   3702      }
   3703 
   3704      case SrcNoteType::SetLine:
   3705        lineno = SrcNote::SetLine::getLine(sn, script->lineno());
   3706        sp->printf(" lineno %u", lineno);
   3707        column = JS::LimitedColumnNumberOneOrigin();
   3708        break;
   3709 
   3710      case SrcNoteType::SetLineColumn:
   3711        lineno = SrcNote::SetLineColumn::getLine(sn, script->lineno());
   3712        column = SrcNote::SetLineColumn::getColumn(sn);
   3713        sp->printf(" lineno %u column %u", lineno, column.oneOriginValue());
   3714        break;
   3715 
   3716      case SrcNoteType::NewLine:
   3717        ++lineno;
   3718        column = JS::LimitedColumnNumberOneOrigin();
   3719        break;
   3720 
   3721      case SrcNoteType::NewLineColumn:
   3722        column = SrcNote::NewLineColumn::getColumn(sn);
   3723        sp->printf(" column %u", column.oneOriginValue());
   3724        ++lineno;
   3725        break;
   3726 
   3727      default:
   3728        MOZ_ASSERT_UNREACHABLE("unrecognized srcnote");
   3729    }
   3730    sp->put("\n");
   3731  }
   3732 
   3733  return true;
   3734 }
   3735 
   3736 static const char* TryNoteName(TryNoteKind kind) {
   3737  switch (kind) {
   3738    case TryNoteKind::Catch:
   3739      return "catch";
   3740    case TryNoteKind::Finally:
   3741      return "finally";
   3742    case TryNoteKind::ForIn:
   3743      return "for-in";
   3744    case TryNoteKind::ForOf:
   3745      return "for-of";
   3746    case TryNoteKind::Loop:
   3747      return "loop";
   3748    case TryNoteKind::ForOfIterClose:
   3749      return "for-of-iterclose";
   3750    case TryNoteKind::Destructuring:
   3751      return "destructuring";
   3752  }
   3753 
   3754  MOZ_CRASH("Bad TryNoteKind");
   3755 }
   3756 
   3757 /* static */
   3758 bool JSScript::dumpTryNotes(JSContext* cx, JS::Handle<JSScript*> script,
   3759                            js::GenericPrinter* sp) {
   3760  sp->put("\nException table:\nkind               stack    start      end\n");
   3761 
   3762  for (const js::TryNote& tn : script->trynotes()) {
   3763    sp->printf(" %-16s %6u %8u %8u\n", TryNoteName(tn.kind()), tn.stackDepth,
   3764               tn.start, tn.start + tn.length);
   3765  }
   3766  return true;
   3767 }
   3768 
   3769 /* static */
   3770 bool JSScript::dumpScopeNotes(JSContext* cx, JS::Handle<JSScript*> script,
   3771                              js::GenericPrinter* sp) {
   3772  sp->put("\nScope notes:\n   index   parent    start      end\n");
   3773 
   3774  for (const ScopeNote& note : script->scopeNotes()) {
   3775    if (note.index == ScopeNote::NoScopeIndex) {
   3776      sp->printf("%8s ", "(none)");
   3777    } else {
   3778      sp->printf("%8u ", note.index.index);
   3779    }
   3780    if (note.parent == ScopeNote::NoScopeIndex) {
   3781      sp->printf("%8s ", "(none)");
   3782    } else {
   3783      sp->printf("%8u ", note.parent);
   3784    }
   3785    sp->printf("%8u %8u\n", note.start, note.start + note.length);
   3786  }
   3787  return true;
   3788 }
   3789 
   3790 /* static */
   3791 bool JSScript::dumpGCThings(JSContext* cx, JS::Handle<JSScript*> script,
   3792                            js::GenericPrinter* sp) {
   3793  sp->put("\nGC things:\n   index   type       value\n");
   3794 
   3795  size_t i = 0;
   3796  for (JS::GCCellPtr gcThing : script->gcthings()) {
   3797    sp->printf("%8zu   ", i);
   3798    if (gcThing.is<JS::BigInt>()) {
   3799      sp->put("BigInt     ");
   3800      gcThing.as<JS::BigInt>().dump(*sp);
   3801      sp->put("\n");
   3802    } else if (gcThing.is<Scope>()) {
   3803      sp->put("Scope      ");
   3804      JS::Rooted<Scope*> scope(cx, &gcThing.as<Scope>());
   3805      if (!Scope::dumpForDisassemble(cx, scope, *sp,
   3806                                     "                      ")) {
   3807        return false;
   3808      }
   3809      sp->put("\n");
   3810    } else if (gcThing.is<JSObject>()) {
   3811      JSObject* obj = &gcThing.as<JSObject>();
   3812      if (obj->is<JSFunction>()) {
   3813        sp->put("Function   ");
   3814        JS::Rooted<JSFunction*> fun(cx, &obj->as<JSFunction>());
   3815        if (fun->fullDisplayAtom()) {
   3816          JS::Rooted<JSAtom*> name(cx, fun->fullDisplayAtom());
   3817          JS::UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, name);
   3818          if (!utf8chars) {
   3819            return false;
   3820          }
   3821          sp->put(utf8chars.get());
   3822        } else {
   3823          sp->put("(anonymous)");
   3824        }
   3825 
   3826        if (fun->hasBaseScript()) {
   3827          BaseScript* script = fun->baseScript();
   3828          sp->printf(" @ %u:%u\n", script->lineno(),
   3829                     script->column().oneOriginValue());
   3830        } else {
   3831          sp->put(" (no script)\n");
   3832        }
   3833      } else {
   3834        if (obj->is<RegExpObject>()) {
   3835          sp->put("RegExp     ");
   3836        } else {
   3837          sp->put("Object     ");
   3838        }
   3839 
   3840        JS::Rooted<JS::Value> objValue(cx, ObjectValue(*obj));
   3841        JS::UniqueChars source = ToDisassemblySource(cx, objValue);
   3842        if (!source) {
   3843          return false;
   3844        }
   3845        sp->put(source.get());
   3846        sp->put("\n");
   3847      }
   3848    } else if (gcThing.is<JSString>()) {
   3849      JS::Rooted<JSString*> str(cx, &gcThing.as<JSString>());
   3850      if (str->isAtom()) {
   3851        sp->put("Atom       ");
   3852      } else {
   3853        sp->put("String     ");
   3854      }
   3855      JS::UniqueChars chars = QuoteString(cx, str, '"');
   3856      if (!chars) {
   3857        return false;
   3858      }
   3859      sp->put(chars.get());
   3860      sp->put("\n");
   3861    } else {
   3862      sp->put("Unknown\n");
   3863    }
   3864    i++;
   3865  }
   3866 
   3867  return true;
   3868 }
   3869 
   3870 #endif  // defined(DEBUG) || defined(JS_JITSPEW)
   3871 
   3872 void JSScript::AutoDelazify::holdScript(JS::HandleFunction fun) {
   3873  if (fun) {
   3874    JSAutoRealm ar(cx_, fun);
   3875    script_ = JSFunction::getOrCreateScript(cx_, fun);
   3876    if (script_) {
   3877      oldAllowRelazify_ = script_->allowRelazify();
   3878      script_->clearAllowRelazify();
   3879    }
   3880  }
   3881 }
   3882 
   3883 void JSScript::AutoDelazify::dropScript() {
   3884  if (script_) {
   3885    script_->setAllowRelazify(oldAllowRelazify_);
   3886  }
   3887  script_ = nullptr;
   3888 }
   3889 
   3890 JS::ubi::Base::Size JS::ubi::Concrete<BaseScript>::size(
   3891    mozilla::MallocSizeOf mallocSizeOf) const {
   3892  BaseScript* base = &get();
   3893 
   3894  Size size = gc::Arena::thingSize(base->getAllocKind());
   3895  size += base->sizeOfExcludingThis(mallocSizeOf);
   3896 
   3897  // Include any JIT data if it exists.
   3898  if (base->hasJitScript()) {
   3899    JSScript* script = base->asJSScript();
   3900 
   3901    size_t jitScriptSize = 0;
   3902    size_t allocSitesSize = 0;
   3903    script->addSizeOfJitScript(mallocSizeOf, &jitScriptSize, &allocSitesSize);
   3904    size += jitScriptSize;
   3905    size += allocSitesSize;
   3906 
   3907    size_t baselineSize = 0;
   3908    jit::AddSizeOfBaselineData(script, mallocSizeOf, &baselineSize);
   3909    size += baselineSize;
   3910 
   3911    size += jit::SizeOfIonData(script, mallocSizeOf);
   3912  }
   3913 
   3914  MOZ_ASSERT(size > 0);
   3915  return size;
   3916 }
   3917 
   3918 const char* JS::ubi::Concrete<BaseScript>::scriptFilename() const {
   3919  return get().filename();
   3920 }