tor-browser

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

SavedStacks.cpp (74416B)


      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 #include "vm/SavedStacks.h"
      8 
      9 #include "mozilla/Attributes.h"
     10 #include "mozilla/DebugOnly.h"
     11 
     12 #include <algorithm>
     13 #include <utility>
     14 
     15 #include "jsapi.h"
     16 #include "jsmath.h"
     17 #include "jsnum.h"
     18 
     19 #include "gc/GCContext.h"
     20 #include "gc/HashUtil.h"
     21 #include "js/CharacterEncoding.h"
     22 #include "js/ColumnNumber.h"  // JS::LimitedColumnNumberOneOrigin, JS::TaggedColumnNumberOneOrigin
     23 #include "js/ErrorReport.h"           // JSErrorBase
     24 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     25 #include "js/PropertyAndElement.h"    // JS_DefineProperty, JS_GetProperty
     26 #include "js/PropertySpec.h"
     27 #include "js/SavedFrameAPI.h"
     28 #include "js/Stack.h"
     29 #include "js/Vector.h"
     30 #include "util/DifferentialTesting.h"
     31 #include "util/StringBuilder.h"
     32 #include "vm/Compartment.h"
     33 #include "vm/FrameIter.h"
     34 #include "vm/GeckoProfiler.h"
     35 #include "vm/JSScript.h"
     36 #include "vm/Realm.h"
     37 #include "vm/SavedFrame.h"
     38 #include "vm/WrapperObject.h"
     39 
     40 #include "debugger/DebugAPI-inl.h"
     41 #include "gc/StableCellHasher-inl.h"
     42 #include "vm/GeckoProfiler-inl.h"
     43 #include "vm/JSContext-inl.h"
     44 
     45 using mozilla::AddToHash;
     46 using mozilla::DebugOnly;
     47 using mozilla::Maybe;
     48 using mozilla::Nothing;
     49 using mozilla::Some;
     50 
     51 namespace js {
     52 
     53 /**
     54 * Maximum number of saved frames returned for an async stack.
     55 */
     56 const uint32_t ASYNC_STACK_MAX_FRAME_COUNT = 60;
     57 
     58 void LiveSavedFrameCache::trace(JSTracer* trc) {
     59  if (!initialized()) {
     60    return;
     61  }
     62 
     63  for (auto* entry = frames->begin(); entry < frames->end(); entry++) {
     64    TraceEdge(trc, &entry->savedFrame,
     65              "LiveSavedFrameCache::frames SavedFrame");
     66  }
     67 }
     68 
     69 bool LiveSavedFrameCache::insert(JSContext* cx, FramePtr&& framePtr,
     70                                 const jsbytecode* pc,
     71                                 Handle<SavedFrame*> savedFrame) {
     72  MOZ_ASSERT(savedFrame);
     73  MOZ_ASSERT(initialized());
     74 
     75 #ifdef DEBUG
     76  // There should not already be an entry for this frame. Checking the full
     77  // stack really slows down some tests, so just check the first and last five
     78  // hundred.
     79  size_t limit = std::min(frames->length() / 2, size_t(500));
     80  for (size_t i = 0; i < limit; i++) {
     81    MOZ_ASSERT(Key(framePtr) != (*frames)[i].key);
     82    MOZ_ASSERT(Key(framePtr) != (*frames)[frames->length() - 1 - i].key);
     83  }
     84 #endif
     85 
     86  if (!frames->emplaceBack(framePtr, pc, savedFrame)) {
     87    ReportOutOfMemory(cx);
     88    return false;
     89  }
     90 
     91  framePtr.setHasCachedSavedFrame();
     92 
     93  return true;
     94 }
     95 
     96 void LiveSavedFrameCache::find(JSContext* cx, FramePtr& framePtr,
     97                               const jsbytecode* pc,
     98                               MutableHandle<SavedFrame*> frame) const {
     99  MOZ_ASSERT(initialized());
    100  MOZ_ASSERT(framePtr.hasCachedSavedFrame());
    101 
    102  // The assertions here check that either 1) frames' hasCachedSavedFrame flags
    103  // accurately indicate the presence of a cache entry for that frame (ignoring
    104  // pc mismatches), or 2) the cache is completely empty, having been flushed
    105  // for a realm mismatch.
    106 
    107  // If we flushed the cache due to a realm mismatch, then we shouldn't
    108  // expect to find any frames in the cache.
    109  if (frames->empty()) {
    110    frame.set(nullptr);
    111    return;
    112  }
    113 
    114  // All our SavedFrames should be in the same realm. If the last
    115  // entry's SavedFrame's realm doesn't match cx's, flush the cache.
    116  if (frames->back().savedFrame->realm() != cx->realm()) {
    117 #ifdef DEBUG
    118    // Check that they are, indeed, all in the same realm.
    119    auto realm = frames->back().savedFrame->realm();
    120    for (const auto& f : (*frames)) {
    121      MOZ_ASSERT(realm == f.savedFrame->realm());
    122    }
    123 #endif
    124    frames->clear();
    125    frame.set(nullptr);
    126    return;
    127  }
    128 
    129  Key key(framePtr);
    130  while (key != frames->back().key) {
    131    MOZ_ASSERT(frames->back().savedFrame->realm() == cx->realm());
    132 
    133    // framePtr must have an entry, but apparently it's below this one on the
    134    // stack; frames->back() must correspond to a frame younger than framePtr's.
    135    // SavedStacks::insertFrames is going to push new cache entries for
    136    // everything younger than framePtr, so this entry should be popped.
    137    frames->popBack();
    138 
    139    // If the frame's bit was set, the frame should always have an entry in
    140    // the cache. (If we purged the entire cache because its SavedFrames had
    141    // been captured for a different realm, then we would have
    142    // returned early above.)
    143    MOZ_RELEASE_ASSERT(!frames->empty());
    144  }
    145 
    146  // The youngest valid frame may have run some code, so its current pc may
    147  // not match its cache entry's pc. In this case, just treat it as a miss. No
    148  // older frame has executed any code; it would have been necessary to pop
    149  // this frame for that to happen, but this frame's bit is set.
    150  if (pc != frames->back().pc) {
    151    frames->popBack();
    152    frame.set(nullptr);
    153    return;
    154  }
    155 
    156  frame.set(frames->back().savedFrame);
    157 }
    158 
    159 void LiveSavedFrameCache::findWithoutInvalidation(
    160    const FramePtr& framePtr, MutableHandle<SavedFrame*> frame) const {
    161  MOZ_ASSERT(initialized());
    162  MOZ_ASSERT(framePtr.hasCachedSavedFrame());
    163 
    164  Key key(framePtr);
    165  for (auto& entry : (*frames)) {
    166    if (entry.key == key) {
    167      frame.set(entry.savedFrame);
    168      return;
    169    }
    170  }
    171 
    172  frame.set(nullptr);
    173 }
    174 
    175 struct MOZ_STACK_CLASS SavedFrame::Lookup {
    176  Lookup(JSAtom* source, uint32_t sourceId, uint32_t line,
    177         JS::TaggedColumnNumberOneOrigin column, JSAtom* functionDisplayName,
    178         JSAtom* asyncCause, SavedFrame* parent, JSPrincipals* principals,
    179         bool mutedErrors,
    180         const Maybe<LiveSavedFrameCache::FramePtr>& framePtr = Nothing(),
    181         jsbytecode* pc = nullptr, Activation* activation = nullptr)
    182      : source(source),
    183        sourceId(sourceId),
    184        line(line),
    185        column(column),
    186        functionDisplayName(functionDisplayName),
    187        asyncCause(asyncCause),
    188        parent(parent),
    189        principals(principals),
    190        mutedErrors(mutedErrors),
    191        framePtr(framePtr),
    192        pc(pc),
    193        activation(activation) {
    194    MOZ_ASSERT(source);
    195    MOZ_ASSERT_IF(framePtr.isSome(), activation);
    196    if (js::SupportDifferentialTesting()) {
    197      this->column = JS::TaggedColumnNumberOneOrigin::forDifferentialTesting();
    198    }
    199  }
    200 
    201  explicit Lookup(SavedFrame& savedFrame)
    202      : source(savedFrame.getSource()),
    203        sourceId(savedFrame.getSourceId()),
    204        line(savedFrame.getLine()),
    205        column(savedFrame.getColumn()),
    206        functionDisplayName(savedFrame.getFunctionDisplayName()),
    207        asyncCause(savedFrame.getAsyncCause()),
    208        parent(savedFrame.getParent()),
    209        principals(savedFrame.getPrincipals()),
    210        mutedErrors(savedFrame.getMutedErrors()),
    211        framePtr(Nothing()),
    212        pc(nullptr),
    213        activation(nullptr) {
    214    MOZ_ASSERT(source);
    215  }
    216 
    217  JSAtom* source;
    218  uint32_t sourceId;
    219 
    220  // Line number (1-origin).
    221  uint32_t line;
    222 
    223  // Columm number in UTF-16 code units.
    224  JS::TaggedColumnNumberOneOrigin column;
    225 
    226  JSAtom* functionDisplayName;
    227  JSAtom* asyncCause;
    228  SavedFrame* parent;
    229  JSPrincipals* principals;
    230  bool mutedErrors;
    231 
    232  // These are used only by the LiveSavedFrameCache and not used for identity or
    233  // hashing.
    234  Maybe<LiveSavedFrameCache::FramePtr> framePtr;
    235  jsbytecode* pc;
    236  Activation* activation;
    237 
    238  void trace(JSTracer* trc) {
    239    TraceRoot(trc, &source, "SavedFrame::Lookup::source");
    240    TraceNullableRoot(trc, &functionDisplayName,
    241                      "SavedFrame::Lookup::functionDisplayName");
    242    TraceNullableRoot(trc, &asyncCause, "SavedFrame::Lookup::asyncCause");
    243    TraceNullableRoot(trc, &parent, "SavedFrame::Lookup::parent");
    244  }
    245 };
    246 
    247 using GCLookupVector =
    248    GCVector<SavedFrame::Lookup, ASYNC_STACK_MAX_FRAME_COUNT>;
    249 
    250 template <class Wrapper>
    251 class WrappedPtrOperations<SavedFrame::Lookup, Wrapper> {
    252  const SavedFrame::Lookup& value() const {
    253    return static_cast<const Wrapper*>(this)->get();
    254  }
    255 
    256 public:
    257  JSAtom* source() { return value().source; }
    258  uint32_t sourceId() { return value().sourceId; }
    259  uint32_t line() { return value().line; }
    260  JS::TaggedColumnNumberOneOrigin column() { return value().column; }
    261  JSAtom* functionDisplayName() { return value().functionDisplayName; }
    262  JSAtom* asyncCause() { return value().asyncCause; }
    263  SavedFrame* parent() { return value().parent; }
    264  JSPrincipals* principals() { return value().principals; }
    265  bool mutedErrors() { return value().mutedErrors; }
    266  Maybe<LiveSavedFrameCache::FramePtr> framePtr() { return value().framePtr; }
    267  jsbytecode* pc() { return value().pc; }
    268  Activation* activation() { return value().activation; }
    269 };
    270 
    271 template <typename Wrapper>
    272 class MutableWrappedPtrOperations<SavedFrame::Lookup, Wrapper>
    273    : public WrappedPtrOperations<SavedFrame::Lookup, Wrapper> {
    274  SavedFrame::Lookup& value() { return static_cast<Wrapper*>(this)->get(); }
    275 
    276 public:
    277  void setParent(SavedFrame* parent) { value().parent = parent; }
    278 
    279  void setAsyncCause(Handle<JSAtom*> asyncCause) {
    280    value().asyncCause = asyncCause;
    281  }
    282 };
    283 
    284 /* static */
    285 bool SavedFrame::HashPolicy::maybeGetHash(const Lookup& l,
    286                                          HashNumber* hashOut) {
    287  HashNumber parentHash;
    288  if (!SavedFramePtrHasher::maybeGetHash(l.parent, &parentHash)) {
    289    return false;
    290  }
    291  *hashOut = calculateHash(l, parentHash);
    292  return true;
    293 }
    294 
    295 /* static */
    296 bool SavedFrame::HashPolicy::ensureHash(const Lookup& l, HashNumber* hashOut) {
    297  HashNumber parentHash;
    298  if (!SavedFramePtrHasher::ensureHash(l.parent, &parentHash)) {
    299    return false;
    300  }
    301  *hashOut = calculateHash(l, parentHash);
    302  return true;
    303 }
    304 
    305 /* static */
    306 HashNumber SavedFrame::HashPolicy::hash(const Lookup& lookup) {
    307  return calculateHash(lookup, SavedFramePtrHasher::hash(lookup.parent));
    308 }
    309 
    310 /* static */
    311 HashNumber SavedFrame::HashPolicy::calculateHash(const Lookup& lookup,
    312                                                 HashNumber parentHash) {
    313  JS::AutoCheckCannotGC nogc;
    314  // Assume that we can take line mod 2^32 without losing anything of
    315  // interest.  If that assumption changes, we'll just need to start with 0
    316  // and add another overload of AddToHash with more arguments.
    317  return AddToHash(lookup.line, lookup.column.rawValue(), lookup.source,
    318                   lookup.functionDisplayName, lookup.asyncCause,
    319                   lookup.mutedErrors, parentHash,
    320                   JSPrincipalsPtrHasher::hash(lookup.principals));
    321 }
    322 
    323 /* static */
    324 bool SavedFrame::HashPolicy::match(SavedFrame* existing, const Lookup& lookup) {
    325  MOZ_ASSERT(existing);
    326 
    327  if (existing->getLine() != lookup.line) {
    328    return false;
    329  }
    330 
    331  if (existing->getColumn() != lookup.column) {
    332    return false;
    333  }
    334 
    335  if (existing->getParent() != lookup.parent) {
    336    return false;
    337  }
    338 
    339  if (existing->getPrincipals() != lookup.principals) {
    340    return false;
    341  }
    342 
    343  JSAtom* source = existing->getSource();
    344  if (source != lookup.source) {
    345    return false;
    346  }
    347 
    348  JSAtom* functionDisplayName = existing->getFunctionDisplayName();
    349  if (functionDisplayName != lookup.functionDisplayName) {
    350    return false;
    351  }
    352 
    353  JSAtom* asyncCause = existing->getAsyncCause();
    354  if (asyncCause != lookup.asyncCause) {
    355    return false;
    356  }
    357 
    358  return true;
    359 }
    360 
    361 /* static */
    362 void SavedFrame::HashPolicy::rekey(Key& key, const Key& newKey) {
    363  key = newKey;
    364 }
    365 
    366 /* static */
    367 bool SavedFrame::finishSavedFrameInit(JSContext* cx, HandleObject ctor,
    368                                      HandleObject proto) {
    369  return FreezeObject(cx, proto);
    370 }
    371 
    372 static const JSClassOps SavedFrameClassOps = {
    373    nullptr,               // addProperty
    374    nullptr,               // delProperty
    375    nullptr,               // enumerate
    376    nullptr,               // newEnumerate
    377    nullptr,               // resolve
    378    nullptr,               // mayResolve
    379    SavedFrame::finalize,  // finalize
    380    nullptr,               // call
    381    nullptr,               // construct
    382    nullptr,               // trace
    383 };
    384 
    385 const ClassSpec SavedFrame::classSpec_ = {
    386    GenericCreateConstructor<SavedFrame::construct, 0, gc::AllocKind::FUNCTION>,
    387    GenericCreatePrototype<SavedFrame>,
    388    SavedFrame::staticFunctions,
    389    nullptr,
    390    SavedFrame::protoFunctions,
    391    SavedFrame::protoAccessors,
    392    SavedFrame::finishSavedFrameInit,
    393    ClassSpec::DontDefineConstructor,
    394 };
    395 
    396 /* static */ const JSClass SavedFrame::class_ = {
    397    "SavedFrame",
    398    JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT) |
    399        JSCLASS_HAS_CACHED_PROTO(JSProto_SavedFrame) |
    400        JSCLASS_FOREGROUND_FINALIZE,
    401    &SavedFrameClassOps,
    402    &SavedFrame::classSpec_,
    403 };
    404 
    405 const JSClass SavedFrame::protoClass_ = {
    406    "SavedFrame.prototype",
    407    JSCLASS_HAS_CACHED_PROTO(JSProto_SavedFrame),
    408    JS_NULL_CLASS_OPS,
    409    &SavedFrame::classSpec_,
    410 };
    411 
    412 /* static */ const JSFunctionSpec SavedFrame::staticFunctions[] = {
    413    JS_FS_END,
    414 };
    415 
    416 /* static */ const JSFunctionSpec SavedFrame::protoFunctions[] = {
    417    JS_FN("constructor", SavedFrame::construct, 0, 0),
    418    JS_FN("toString", SavedFrame::toStringMethod, 0, 0),
    419    JS_FS_END,
    420 };
    421 
    422 /* static */ const JSPropertySpec SavedFrame::protoAccessors[] = {
    423    JS_PSG("source", SavedFrame::sourceProperty, 0),
    424    JS_PSG("sourceId", SavedFrame::sourceIdProperty, 0),
    425    JS_PSG("line", SavedFrame::lineProperty, 0),
    426    JS_PSG("column", SavedFrame::columnProperty, 0),
    427    JS_PSG("functionDisplayName", SavedFrame::functionDisplayNameProperty, 0),
    428    JS_PSG("asyncCause", SavedFrame::asyncCauseProperty, 0),
    429    JS_PSG("asyncParent", SavedFrame::asyncParentProperty, 0),
    430    JS_PSG("parent", SavedFrame::parentProperty, 0),
    431    JS_STRING_SYM_PS(toStringTag, "SavedFrame", JSPROP_READONLY),
    432    JS_PS_END,
    433 };
    434 
    435 /* static */
    436 void SavedFrame::finalize(JS::GCContext* gcx, JSObject* obj) {
    437  MOZ_ASSERT(gcx->onMainThread());
    438  JSPrincipals* p = obj->as<SavedFrame>().getPrincipals();
    439  if (p) {
    440    JSRuntime* rt = obj->runtimeFromMainThread();
    441    JS_DropPrincipals(rt->mainContextFromOwnThread(), p);
    442  }
    443 }
    444 
    445 JSAtom* SavedFrame::getSource() {
    446  const Value& v = getReservedSlot(JSSLOT_SOURCE);
    447  JSString* s = v.toString();
    448  return &s->asAtom();
    449 }
    450 
    451 uint32_t SavedFrame::getSourceId() {
    452  const Value& v = getReservedSlot(JSSLOT_SOURCEID);
    453  return v.toPrivateUint32();
    454 }
    455 
    456 uint32_t SavedFrame::getLine() {
    457  const Value& v = getReservedSlot(JSSLOT_LINE);
    458  return v.toPrivateUint32();
    459 }
    460 
    461 JS::TaggedColumnNumberOneOrigin SavedFrame::getColumn() {
    462  const Value& v = getReservedSlot(JSSLOT_COLUMN);
    463  return JS::TaggedColumnNumberOneOrigin::fromRaw(v.toPrivateUint32());
    464 }
    465 
    466 JSAtom* SavedFrame::getFunctionDisplayName() {
    467  const Value& v = getReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME);
    468  if (v.isNull()) {
    469    return nullptr;
    470  }
    471  JSString* s = v.toString();
    472  return &s->asAtom();
    473 }
    474 
    475 JSAtom* SavedFrame::getAsyncCause() {
    476  const Value& v = getReservedSlot(JSSLOT_ASYNCCAUSE);
    477  if (v.isNull()) {
    478    return nullptr;
    479  }
    480  JSString* s = v.toString();
    481  return &s->asAtom();
    482 }
    483 
    484 SavedFrame* SavedFrame::getParent() const {
    485  const Value& v = getReservedSlot(JSSLOT_PARENT);
    486  return v.isObject() ? &v.toObject().as<SavedFrame>() : nullptr;
    487 }
    488 
    489 JSPrincipals* SavedFrame::getPrincipals() {
    490  const Value& v = getReservedSlot(JSSLOT_PRINCIPALS);
    491  if (v.isUndefined()) {
    492    return nullptr;
    493  }
    494  return reinterpret_cast<JSPrincipals*>(uintptr_t(v.toPrivate()) & ~0b1);
    495 }
    496 
    497 bool SavedFrame::getMutedErrors() {
    498  const Value& v = getReservedSlot(JSSLOT_PRINCIPALS);
    499  if (v.isUndefined()) {
    500    return true;
    501  }
    502  return bool(uintptr_t(v.toPrivate()) & 0b1);
    503 }
    504 
    505 void SavedFrame::initSource(JSAtom* source) {
    506  MOZ_ASSERT(source);
    507  initReservedSlot(JSSLOT_SOURCE, StringValue(source));
    508 }
    509 
    510 void SavedFrame::initSourceId(uint32_t sourceId) {
    511  initReservedSlot(JSSLOT_SOURCEID, PrivateUint32Value(sourceId));
    512 }
    513 
    514 void SavedFrame::initLine(uint32_t line) {
    515  initReservedSlot(JSSLOT_LINE, PrivateUint32Value(line));
    516 }
    517 
    518 void SavedFrame::initColumn(JS::TaggedColumnNumberOneOrigin column) {
    519  if (js::SupportDifferentialTesting()) {
    520    column = JS::TaggedColumnNumberOneOrigin::forDifferentialTesting();
    521  }
    522  initReservedSlot(JSSLOT_COLUMN, PrivateUint32Value(column.rawValue()));
    523 }
    524 
    525 void SavedFrame::initPrincipalsAndMutedErrors(JSPrincipals* principals,
    526                                              bool mutedErrors) {
    527  if (principals) {
    528    JS_HoldPrincipals(principals);
    529  }
    530  initPrincipalsAlreadyHeldAndMutedErrors(principals, mutedErrors);
    531 }
    532 
    533 void SavedFrame::initPrincipalsAlreadyHeldAndMutedErrors(
    534    JSPrincipals* principals, bool mutedErrors) {
    535  MOZ_ASSERT_IF(principals, principals->refcount > 0);
    536  uintptr_t ptr = uintptr_t(principals) | mutedErrors;
    537  initReservedSlot(JSSLOT_PRINCIPALS,
    538                   PrivateValue(reinterpret_cast<void*>(ptr)));
    539 }
    540 
    541 void SavedFrame::initFunctionDisplayName(JSAtom* maybeName) {
    542  initReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME,
    543                   maybeName ? StringValue(maybeName) : NullValue());
    544 }
    545 
    546 void SavedFrame::initAsyncCause(JSAtom* maybeCause) {
    547  initReservedSlot(JSSLOT_ASYNCCAUSE,
    548                   maybeCause ? StringValue(maybeCause) : NullValue());
    549 }
    550 
    551 void SavedFrame::initParent(SavedFrame* maybeParent) {
    552  initReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(maybeParent));
    553 }
    554 
    555 void SavedFrame::initFromLookup(JSContext* cx, Handle<Lookup> lookup) {
    556  // Make sure any atoms used in the lookup are marked in the current zone.
    557  // Normally we would try to keep these mark bits up to date around the
    558  // points where the context moves between compartments, but Lookups live on
    559  // the stack (where the atoms are kept alive regardless) and this is a
    560  // more convenient pinchpoint.
    561  if (lookup.source()) {
    562    cx->markAtom(lookup.source());
    563  }
    564  if (lookup.functionDisplayName()) {
    565    cx->markAtom(lookup.functionDisplayName());
    566  }
    567  if (lookup.asyncCause()) {
    568    cx->markAtom(lookup.asyncCause());
    569  }
    570 
    571  initSource(lookup.source());
    572  initSourceId(lookup.sourceId());
    573  initLine(lookup.line());
    574  initColumn(lookup.column());
    575  initFunctionDisplayName(lookup.functionDisplayName());
    576  initAsyncCause(lookup.asyncCause());
    577  initParent(lookup.parent());
    578  initPrincipalsAndMutedErrors(lookup.principals(), lookup.mutedErrors());
    579 }
    580 
    581 /* static */
    582 SavedFrame* SavedFrame::create(JSContext* cx) {
    583  Rooted<GlobalObject*> global(cx, cx->global());
    584  cx->check(global);
    585 
    586  // Ensure that we don't try to capture the stack again in the
    587  // `SavedStacksMetadataBuilder` for this new SavedFrame object, and
    588  // accidentally cause O(n^2) behavior.
    589  SavedStacks::AutoReentrancyGuard guard(cx->realm()->savedStacks());
    590 
    591  RootedObject proto(cx,
    592                     GlobalObject::getOrCreateSavedFramePrototype(cx, global));
    593  if (!proto) {
    594    return nullptr;
    595  }
    596  cx->check(proto);
    597 
    598  return NewTenuredObjectWithGivenProto<SavedFrame>(cx, proto);
    599 }
    600 
    601 bool SavedFrame::isSelfHosted(JSContext* cx) {
    602  JSAtom* source = getSource();
    603  return source == cx->names().self_hosted_;
    604 }
    605 
    606 bool SavedFrame::isWasm() { return getColumn().isWasmFunctionIndex(); }
    607 
    608 uint32_t SavedFrame::wasmFuncIndex() {
    609  return getColumn().toWasmFunctionIndex().value();
    610 }
    611 
    612 uint32_t SavedFrame::wasmBytecodeOffset() {
    613  MOZ_ASSERT(isWasm());
    614  return getLine();
    615 }
    616 
    617 /* static */
    618 bool SavedFrame::construct(JSContext* cx, unsigned argc, Value* vp) {
    619  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
    620                            "SavedFrame");
    621  return false;
    622 }
    623 
    624 static bool SavedFrameSubsumedByPrincipals(JSContext* cx,
    625                                           JSPrincipals* principals,
    626                                           Handle<SavedFrame*> frame) {
    627  auto subsumes = cx->runtime()->securityCallbacks->subsumes;
    628  if (!subsumes) {
    629    return true;
    630  }
    631 
    632  MOZ_ASSERT(!ReconstructedSavedFramePrincipals::is(principals));
    633 
    634  auto framePrincipals = frame->getPrincipals();
    635 
    636  // Handle SavedFrames that have been reconstructed from stacks in a heap
    637  // snapshot.
    638  if (framePrincipals == &ReconstructedSavedFramePrincipals::IsSystem) {
    639    return cx->runningWithTrustedPrincipals();
    640  }
    641  if (framePrincipals == &ReconstructedSavedFramePrincipals::IsNotSystem) {
    642    return true;
    643  }
    644 
    645  return subsumes(principals, framePrincipals);
    646 }
    647 
    648 // Return the first SavedFrame in the chain that starts with |frame| whose
    649 // for which the given match function returns true. If there is no such frame,
    650 // return nullptr. |skippedAsync| is set to true if any of the skipped frames
    651 // had the |asyncCause| property set, otherwise it is explicitly set to false.
    652 template <typename Matcher>
    653 static SavedFrame* GetFirstMatchedFrame(JSContext* cx, JSPrincipals* principals,
    654                                        Matcher& matches,
    655                                        Handle<SavedFrame*> frame,
    656                                        JS::SavedFrameSelfHosted selfHosted,
    657                                        bool& skippedAsync) {
    658  skippedAsync = false;
    659 
    660  Rooted<SavedFrame*> rootedFrame(cx, frame);
    661  while (rootedFrame) {
    662    if ((selfHosted == JS::SavedFrameSelfHosted::Include ||
    663         !rootedFrame->isSelfHosted(cx)) &&
    664        matches(cx, principals, rootedFrame)) {
    665      return rootedFrame;
    666    }
    667 
    668    if (rootedFrame->getAsyncCause()) {
    669      skippedAsync = true;
    670    }
    671 
    672    rootedFrame = rootedFrame->getParent();
    673  }
    674 
    675  return nullptr;
    676 }
    677 
    678 // Return the first SavedFrame in the chain that starts with |frame| whose
    679 // principals are subsumed by |principals|, according to |subsumes|. If there is
    680 // no such frame, return nullptr. |skippedAsync| is set to true if any of the
    681 // skipped frames had the |asyncCause| property set, otherwise it is explicitly
    682 // set to false.
    683 static SavedFrame* GetFirstSubsumedFrame(JSContext* cx,
    684                                         JSPrincipals* principals,
    685                                         Handle<SavedFrame*> frame,
    686                                         JS::SavedFrameSelfHosted selfHosted,
    687                                         bool& skippedAsync) {
    688  return GetFirstMatchedFrame(cx, principals, SavedFrameSubsumedByPrincipals,
    689                              frame, selfHosted, skippedAsync);
    690 }
    691 
    692 JS_PUBLIC_API JSObject* GetFirstSubsumedSavedFrame(
    693    JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
    694    JS::SavedFrameSelfHosted selfHosted) {
    695  if (!savedFrame) {
    696    return nullptr;
    697  }
    698 
    699  auto subsumes = cx->runtime()->securityCallbacks->subsumes;
    700  if (!subsumes) {
    701    return nullptr;
    702  }
    703 
    704  auto matcher = [subsumes](JSContext* cx, JSPrincipals* principals,
    705                            Handle<SavedFrame*> frame) -> bool {
    706    return subsumes(principals, frame->getPrincipals());
    707  };
    708 
    709  bool skippedAsync;
    710  Rooted<SavedFrame*> frame(cx, &savedFrame->as<SavedFrame>());
    711  return GetFirstMatchedFrame(cx, principals, matcher, frame, selfHosted,
    712                              skippedAsync);
    713 }
    714 
    715 [[nodiscard]] static bool SavedFrame_checkThis(JSContext* cx, CallArgs& args,
    716                                               const char* fnName,
    717                                               MutableHandleObject frame) {
    718  const Value& thisValue = args.thisv();
    719 
    720  if (!thisValue.isObject()) {
    721    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    722                              JSMSG_OBJECT_REQUIRED,
    723                              InformalValueTypeName(thisValue));
    724    return false;
    725  }
    726 
    727  if (!thisValue.toObject().canUnwrapAs<SavedFrame>()) {
    728    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    729                              JSMSG_INCOMPATIBLE_PROTO, SavedFrame::class_.name,
    730                              fnName, "object");
    731    return false;
    732  }
    733 
    734  // Now set "frame" to the actual object we were invoked in (which may be a
    735  // wrapper), not the unwrapped version.  Consumers will need to know what
    736  // that original object was, and will do principal checks as needed.
    737  frame.set(&thisValue.toObject());
    738  return true;
    739 }
    740 
    741 // Get the SavedFrame * from the current this value and handle any errors that
    742 // might occur therein.
    743 //
    744 // These parameters must already exist when calling this macro:
    745 //   - JSContext* cx
    746 //   - unsigned   argc
    747 //   - Value*     vp
    748 //   - const char* fnName
    749 // These parameters will be defined after calling this macro:
    750 //   - CallArgs args
    751 //   - Rooted<SavedFrame*> frame (will be non-null)
    752 #define THIS_SAVEDFRAME(cx, argc, vp, fnName, args, frame) \
    753  CallArgs args = CallArgsFromVp(argc, vp);                \
    754  RootedObject frame(cx);                                  \
    755  if (!SavedFrame_checkThis(cx, args, fnName, &frame)) return false;
    756 
    757 } /* namespace js */
    758 
    759 js::SavedFrame* js::UnwrapSavedFrame(JSContext* cx, JSPrincipals* principals,
    760                                     HandleObject obj,
    761                                     JS::SavedFrameSelfHosted selfHosted,
    762                                     bool& skippedAsync) {
    763  if (!obj) {
    764    return nullptr;
    765  }
    766 
    767  Rooted<SavedFrame*> frame(cx, obj->maybeUnwrapAs<SavedFrame>());
    768  if (!frame) {
    769    return nullptr;
    770  }
    771 
    772  return GetFirstSubsumedFrame(cx, principals, frame, selfHosted, skippedAsync);
    773 }
    774 
    775 namespace JS {
    776 
    777 JS_PUBLIC_API SavedFrameResult GetSavedFrameSource(
    778    JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
    779    MutableHandleString sourcep,
    780    SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */) {
    781  js::AssertHeapIsIdle();
    782  CHECK_THREAD(cx);
    783  MOZ_RELEASE_ASSERT(cx->realm());
    784 
    785  {
    786    bool skippedAsync;
    787    Rooted<js::SavedFrame*> frame(
    788        cx,
    789        UnwrapSavedFrame(cx, principals, savedFrame, selfHosted, skippedAsync));
    790    if (!frame) {
    791      sourcep.set(cx->runtime()->emptyString);
    792      return SavedFrameResult::AccessDenied;
    793    }
    794    sourcep.set(frame->getSource());
    795  }
    796  if (sourcep->isAtom()) {
    797    cx->markAtom(&sourcep->asAtom());
    798  }
    799  return SavedFrameResult::Ok;
    800 }
    801 
    802 JS_PUBLIC_API SavedFrameResult GetSavedFrameSourceId(
    803    JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
    804    uint32_t* sourceIdp,
    805    SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */) {
    806  js::AssertHeapIsIdle();
    807  CHECK_THREAD(cx);
    808  MOZ_RELEASE_ASSERT(cx->realm());
    809 
    810  bool skippedAsync;
    811  Rooted<js::SavedFrame*> frame(cx, UnwrapSavedFrame(cx, principals, savedFrame,
    812                                                     selfHosted, skippedAsync));
    813  if (!frame) {
    814    *sourceIdp = 0;
    815    return SavedFrameResult::AccessDenied;
    816  }
    817  *sourceIdp = frame->getSourceId();
    818  return SavedFrameResult::Ok;
    819 }
    820 
    821 JS_PUBLIC_API SavedFrameResult GetSavedFrameLine(
    822    JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
    823    uint32_t* linep,
    824    SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */) {
    825  js::AssertHeapIsIdle();
    826  CHECK_THREAD(cx);
    827  MOZ_RELEASE_ASSERT(cx->realm());
    828  MOZ_ASSERT(linep);
    829 
    830  bool skippedAsync;
    831  Rooted<js::SavedFrame*> frame(cx, UnwrapSavedFrame(cx, principals, savedFrame,
    832                                                     selfHosted, skippedAsync));
    833  if (!frame) {
    834    *linep = 0;
    835    return SavedFrameResult::AccessDenied;
    836  }
    837  *linep = frame->getLine();
    838  return SavedFrameResult::Ok;
    839 }
    840 
    841 JS_PUBLIC_API SavedFrameResult GetSavedFrameColumn(
    842    JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
    843    JS::TaggedColumnNumberOneOrigin* columnp,
    844    SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */) {
    845  js::AssertHeapIsIdle();
    846  CHECK_THREAD(cx);
    847  MOZ_RELEASE_ASSERT(cx->realm());
    848  MOZ_ASSERT(columnp);
    849 
    850  bool skippedAsync;
    851  Rooted<js::SavedFrame*> frame(cx, UnwrapSavedFrame(cx, principals, savedFrame,
    852                                                     selfHosted, skippedAsync));
    853  if (!frame) {
    854    *columnp = JS::TaggedColumnNumberOneOrigin();
    855    return SavedFrameResult::AccessDenied;
    856  }
    857  *columnp = frame->getColumn();
    858  return SavedFrameResult::Ok;
    859 }
    860 
    861 JS_PUBLIC_API SavedFrameResult GetSavedFrameFunctionDisplayName(
    862    JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
    863    MutableHandleString namep,
    864    SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */) {
    865  js::AssertHeapIsIdle();
    866  CHECK_THREAD(cx);
    867  MOZ_RELEASE_ASSERT(cx->realm());
    868 
    869  {
    870    bool skippedAsync;
    871    Rooted<js::SavedFrame*> frame(
    872        cx,
    873        UnwrapSavedFrame(cx, principals, savedFrame, selfHosted, skippedAsync));
    874    if (!frame) {
    875      namep.set(nullptr);
    876      return SavedFrameResult::AccessDenied;
    877    }
    878    namep.set(frame->getFunctionDisplayName());
    879  }
    880  if (namep && namep->isAtom()) {
    881    cx->markAtom(&namep->asAtom());
    882  }
    883  return SavedFrameResult::Ok;
    884 }
    885 
    886 JS_PUBLIC_API SavedFrameResult GetSavedFrameAsyncCause(
    887    JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
    888    MutableHandleString asyncCausep,
    889    SavedFrameSelfHosted unused_ /* = SavedFrameSelfHosted::Include */) {
    890  js::AssertHeapIsIdle();
    891  CHECK_THREAD(cx);
    892  MOZ_RELEASE_ASSERT(cx->realm());
    893 
    894  {
    895    bool skippedAsync;
    896    // This function is always called with self-hosted frames excluded by
    897    // GetValueIfNotCached in dom/bindings/Exceptions.cpp. However, we want
    898    // to include them because our Promise implementation causes us to have
    899    // the async cause on a self-hosted frame. So we just ignore the
    900    // parameter and always include self-hosted frames.
    901    Rooted<js::SavedFrame*> frame(
    902        cx, UnwrapSavedFrame(cx, principals, savedFrame,
    903                             SavedFrameSelfHosted::Include, skippedAsync));
    904    if (!frame) {
    905      asyncCausep.set(nullptr);
    906      return SavedFrameResult::AccessDenied;
    907    }
    908    asyncCausep.set(frame->getAsyncCause());
    909    if (!asyncCausep && skippedAsync) {
    910      asyncCausep.set(cx->names().Async);
    911    }
    912  }
    913  if (asyncCausep && asyncCausep->isAtom()) {
    914    cx->markAtom(&asyncCausep->asAtom());
    915  }
    916  return SavedFrameResult::Ok;
    917 }
    918 
    919 JS_PUBLIC_API SavedFrameResult GetSavedFrameAsyncParent(
    920    JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
    921    MutableHandleObject asyncParentp,
    922    SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */) {
    923  js::AssertHeapIsIdle();
    924  CHECK_THREAD(cx);
    925  MOZ_RELEASE_ASSERT(cx->realm());
    926 
    927  bool skippedAsync;
    928  Rooted<js::SavedFrame*> frame(cx, UnwrapSavedFrame(cx, principals, savedFrame,
    929                                                     selfHosted, skippedAsync));
    930  if (!frame) {
    931    asyncParentp.set(nullptr);
    932    return SavedFrameResult::AccessDenied;
    933  }
    934  Rooted<js::SavedFrame*> parent(cx, frame->getParent());
    935 
    936  // The current value of |skippedAsync| is not interesting, because we are
    937  // interested in whether we would cross any async parents to get from here
    938  // to the first subsumed parent frame instead.
    939  Rooted<js::SavedFrame*> subsumedParent(
    940      cx,
    941      GetFirstSubsumedFrame(cx, principals, parent, selfHosted, skippedAsync));
    942 
    943  // Even if |parent| is not subsumed, we still want to return a pointer to it
    944  // rather than |subsumedParent| so it can pick up any |asyncCause| from the
    945  // inaccessible part of the chain.
    946  if (subsumedParent && (subsumedParent->getAsyncCause() || skippedAsync)) {
    947    asyncParentp.set(parent);
    948  } else {
    949    asyncParentp.set(nullptr);
    950  }
    951  return SavedFrameResult::Ok;
    952 }
    953 
    954 JS_PUBLIC_API SavedFrameResult GetSavedFrameParent(
    955    JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
    956    MutableHandleObject parentp,
    957    SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */) {
    958  js::AssertHeapIsIdle();
    959  CHECK_THREAD(cx);
    960  MOZ_RELEASE_ASSERT(cx->realm());
    961 
    962  bool skippedAsync;
    963  Rooted<js::SavedFrame*> frame(cx, UnwrapSavedFrame(cx, principals, savedFrame,
    964                                                     selfHosted, skippedAsync));
    965  if (!frame) {
    966    parentp.set(nullptr);
    967    return SavedFrameResult::AccessDenied;
    968  }
    969  Rooted<js::SavedFrame*> parent(cx, frame->getParent());
    970 
    971  // The current value of |skippedAsync| is not interesting, because we are
    972  // interested in whether we would cross any async parents to get from here
    973  // to the first subsumed parent frame instead.
    974  Rooted<js::SavedFrame*> subsumedParent(
    975      cx,
    976      GetFirstSubsumedFrame(cx, principals, parent, selfHosted, skippedAsync));
    977 
    978  // Even if |parent| is not subsumed, we still want to return a pointer to it
    979  // rather than |subsumedParent| so it can pick up any |asyncCause| from the
    980  // inaccessible part of the chain.
    981  if (subsumedParent && !(subsumedParent->getAsyncCause() || skippedAsync)) {
    982    parentp.set(parent);
    983  } else {
    984    parentp.set(nullptr);
    985  }
    986  return SavedFrameResult::Ok;
    987 }
    988 
    989 static bool FormatStackFrameLine(js::StringBuilder& sb,
    990                                 JS::Handle<js::SavedFrame*> frame) {
    991  if (frame->isWasm()) {
    992    // See comment in WasmFrameIter::computeLine().
    993    return sb.append("wasm-function[") &&
    994           NumberValueToStringBuilder(NumberValue(frame->wasmFuncIndex()),
    995                                      sb) &&
    996           sb.append(']');
    997  }
    998 
    999  return NumberValueToStringBuilder(NumberValue(frame->getLine()), sb);
   1000 }
   1001 
   1002 static bool FormatStackFrameColumn(js::StringBuilder& sb,
   1003                                   JS::Handle<js::SavedFrame*> frame) {
   1004  if (frame->isWasm()) {
   1005    // See comment in WasmFrameIter::computeLine().
   1006    js::Int32ToCStringBuf cbuf;
   1007    size_t cstrlen;
   1008    const char* cstr =
   1009        Uint32ToHexCString(&cbuf, frame->wasmBytecodeOffset(), &cstrlen);
   1010    MOZ_ASSERT(cstr);
   1011 
   1012    return sb.append("0x") && sb.append(cstr, cstrlen);
   1013  }
   1014 
   1015  return NumberValueToStringBuilder(
   1016      NumberValue(frame->getColumn().oneOriginValue()), sb);
   1017 }
   1018 
   1019 static bool FormatSpiderMonkeyStackFrame(JSContext* cx, js::StringBuilder& sb,
   1020                                         JS::Handle<js::SavedFrame*> frame,
   1021                                         size_t indent, bool skippedAsync) {
   1022  RootedString asyncCause(cx, frame->getAsyncCause());
   1023  if (!asyncCause && skippedAsync) {
   1024    asyncCause.set(cx->names().Async);
   1025  }
   1026 
   1027  Rooted<JSAtom*> name(cx, frame->getFunctionDisplayName());
   1028  return (!indent || sb.appendN(' ', indent)) &&
   1029         (!asyncCause || (sb.append(asyncCause) && sb.append('*'))) &&
   1030         (!name || sb.append(name)) && sb.append('@') &&
   1031         sb.append(frame->getSource()) && sb.append(':') &&
   1032         FormatStackFrameLine(sb, frame) && sb.append(':') &&
   1033         FormatStackFrameColumn(sb, frame) && sb.append('\n');
   1034 }
   1035 
   1036 static bool FormatV8StackFrame(JSContext* cx, js::StringBuilder& sb,
   1037                               JS::Handle<js::SavedFrame*> frame, size_t indent,
   1038                               bool lastFrame) {
   1039  Rooted<JSAtom*> name(cx, frame->getFunctionDisplayName());
   1040  return sb.appendN(' ', indent + 4) && sb.append('a') && sb.append('t') &&
   1041         sb.append(' ') &&
   1042         (!name || (sb.append(name) && sb.append(' ') && sb.append('('))) &&
   1043         sb.append(frame->getSource()) && sb.append(':') &&
   1044         FormatStackFrameLine(sb, frame) && sb.append(':') &&
   1045         FormatStackFrameColumn(sb, frame) && (!name || sb.append(')')) &&
   1046         (lastFrame || sb.append('\n'));
   1047 }
   1048 
   1049 JS_PUBLIC_API bool BuildStackString(JSContext* cx, JSPrincipals* principals,
   1050                                    HandleObject stack,
   1051                                    MutableHandleString stringp, size_t indent,
   1052                                    js::StackFormat format) {
   1053  js::AssertHeapIsIdle();
   1054  CHECK_THREAD(cx);
   1055  MOZ_RELEASE_ASSERT(cx->realm());
   1056 
   1057  js::JSStringBuilder sb(cx);
   1058 
   1059  if (format == js::StackFormat::Default) {
   1060    format = cx->runtime()->stackFormat();
   1061  }
   1062  MOZ_ASSERT(format != js::StackFormat::Default);
   1063 
   1064  // Enter a new block to constrain the scope of possibly entering the stack's
   1065  // realm. This ensures that when we finish the StringBuilder, we are back in
   1066  // the cx's original compartment, and fulfill our contract with callers to
   1067  // place the output string in the cx's current realm.
   1068  {
   1069    bool skippedAsync;
   1070    Rooted<js::SavedFrame*> frame(
   1071        cx, UnwrapSavedFrame(cx, principals, stack,
   1072                             SavedFrameSelfHosted::Exclude, skippedAsync));
   1073    if (!frame) {
   1074      stringp.set(cx->runtime()->emptyString);
   1075      return true;
   1076    }
   1077 
   1078    Rooted<js::SavedFrame*> parent(cx);
   1079    do {
   1080      MOZ_ASSERT(SavedFrameSubsumedByPrincipals(cx, principals, frame));
   1081      MOZ_ASSERT(!frame->isSelfHosted(cx));
   1082 
   1083      parent = frame->getParent();
   1084      bool skippedNextAsync;
   1085      Rooted<js::SavedFrame*> nextFrame(
   1086          cx, js::GetFirstSubsumedFrame(cx, principals, parent,
   1087                                        SavedFrameSelfHosted::Exclude,
   1088                                        skippedNextAsync));
   1089 
   1090      switch (format) {
   1091        case js::StackFormat::SpiderMonkey:
   1092          if (!FormatSpiderMonkeyStackFrame(cx, sb, frame, indent,
   1093                                            skippedAsync)) {
   1094            return false;
   1095          }
   1096          break;
   1097        case js::StackFormat::V8:
   1098          if (!FormatV8StackFrame(cx, sb, frame, indent, !nextFrame)) {
   1099            return false;
   1100          }
   1101          break;
   1102        case js::StackFormat::Default:
   1103          MOZ_CRASH("Unexpected value");
   1104          break;
   1105      }
   1106 
   1107      frame = nextFrame;
   1108      skippedAsync = skippedNextAsync;
   1109    } while (frame);
   1110  }
   1111 
   1112  JSString* str = sb.finishString();
   1113  if (!str) {
   1114    return false;
   1115  }
   1116  cx->check(str);
   1117  stringp.set(str);
   1118  return true;
   1119 }
   1120 
   1121 JS_PUBLIC_API bool IsMaybeWrappedSavedFrame(JSObject* obj) {
   1122  MOZ_ASSERT(obj);
   1123  return obj->canUnwrapAs<js::SavedFrame>();
   1124 }
   1125 
   1126 JS_PUBLIC_API bool IsUnwrappedSavedFrame(JSObject* obj) {
   1127  MOZ_ASSERT(obj);
   1128  return obj->is<js::SavedFrame>();
   1129 }
   1130 
   1131 static bool AssignProperty(JSContext* cx, HandleObject dst, HandleObject src,
   1132                           const char* property) {
   1133  RootedValue v(cx);
   1134  return JS_GetProperty(cx, src, property, &v) &&
   1135         JS_DefineProperty(cx, dst, property, v, JSPROP_ENUMERATE);
   1136 }
   1137 
   1138 JS_PUBLIC_API JSObject* ConvertSavedFrameToPlainObject(
   1139    JSContext* cx, HandleObject savedFrameArg,
   1140    SavedFrameSelfHosted selfHosted) {
   1141  MOZ_ASSERT(savedFrameArg);
   1142 
   1143  RootedObject savedFrame(cx, savedFrameArg);
   1144  RootedObject baseConverted(cx), lastConverted(cx);
   1145  RootedValue v(cx);
   1146 
   1147  baseConverted = lastConverted = JS_NewObject(cx, nullptr);
   1148  if (!baseConverted) {
   1149    return nullptr;
   1150  }
   1151 
   1152  bool foundParent;
   1153  do {
   1154    if (!AssignProperty(cx, lastConverted, savedFrame, "source") ||
   1155        !AssignProperty(cx, lastConverted, savedFrame, "sourceId") ||
   1156        !AssignProperty(cx, lastConverted, savedFrame, "line") ||
   1157        !AssignProperty(cx, lastConverted, savedFrame, "column") ||
   1158        !AssignProperty(cx, lastConverted, savedFrame, "functionDisplayName") ||
   1159        !AssignProperty(cx, lastConverted, savedFrame, "asyncCause")) {
   1160      return nullptr;
   1161    }
   1162 
   1163    const char* parentProperties[] = {"parent", "asyncParent"};
   1164    foundParent = false;
   1165    for (const char* prop : parentProperties) {
   1166      if (!JS_GetProperty(cx, savedFrame, prop, &v)) {
   1167        return nullptr;
   1168      }
   1169      if (v.isObject()) {
   1170        RootedObject nextConverted(cx, JS_NewObject(cx, nullptr));
   1171        if (!nextConverted ||
   1172            !JS_DefineProperty(cx, lastConverted, prop, nextConverted,
   1173                               JSPROP_ENUMERATE)) {
   1174          return nullptr;
   1175        }
   1176        lastConverted = nextConverted;
   1177        savedFrame = &v.toObject();
   1178        foundParent = true;
   1179        break;
   1180      }
   1181    }
   1182  } while (foundParent);
   1183 
   1184  return baseConverted;
   1185 }
   1186 
   1187 } /* namespace JS */
   1188 
   1189 namespace js {
   1190 
   1191 /* static */
   1192 bool SavedFrame::sourceProperty(JSContext* cx, unsigned argc, Value* vp) {
   1193  THIS_SAVEDFRAME(cx, argc, vp, "(get source)", args, frame);
   1194  JSPrincipals* principals = cx->realm()->principals();
   1195  RootedString source(cx);
   1196  if (JS::GetSavedFrameSource(cx, principals, frame, &source) ==
   1197      JS::SavedFrameResult::Ok) {
   1198    if (!cx->compartment()->wrap(cx, &source)) {
   1199      return false;
   1200    }
   1201    args.rval().setString(source);
   1202  } else {
   1203    args.rval().setNull();
   1204  }
   1205  return true;
   1206 }
   1207 
   1208 /* static */
   1209 bool SavedFrame::sourceIdProperty(JSContext* cx, unsigned argc, Value* vp) {
   1210  THIS_SAVEDFRAME(cx, argc, vp, "(get sourceId)", args, frame);
   1211  JSPrincipals* principals = cx->realm()->principals();
   1212  uint32_t sourceId;
   1213  if (JS::GetSavedFrameSourceId(cx, principals, frame, &sourceId) ==
   1214      JS::SavedFrameResult::Ok) {
   1215    args.rval().setNumber(sourceId);
   1216  } else {
   1217    args.rval().setNull();
   1218  }
   1219  return true;
   1220 }
   1221 
   1222 /* static */
   1223 bool SavedFrame::lineProperty(JSContext* cx, unsigned argc, Value* vp) {
   1224  THIS_SAVEDFRAME(cx, argc, vp, "(get line)", args, frame);
   1225  JSPrincipals* principals = cx->realm()->principals();
   1226  uint32_t line;
   1227  if (JS::GetSavedFrameLine(cx, principals, frame, &line) ==
   1228      JS::SavedFrameResult::Ok) {
   1229    args.rval().setNumber(line);
   1230  } else {
   1231    args.rval().setNull();
   1232  }
   1233  return true;
   1234 }
   1235 
   1236 /* static */
   1237 bool SavedFrame::columnProperty(JSContext* cx, unsigned argc, Value* vp) {
   1238  THIS_SAVEDFRAME(cx, argc, vp, "(get column)", args, frame);
   1239  JSPrincipals* principals = cx->realm()->principals();
   1240  JS::TaggedColumnNumberOneOrigin column;
   1241  if (JS::GetSavedFrameColumn(cx, principals, frame, &column) ==
   1242      JS::SavedFrameResult::Ok) {
   1243    args.rval().setNumber(column.oneOriginValue());
   1244  } else {
   1245    args.rval().setNull();
   1246  }
   1247  return true;
   1248 }
   1249 
   1250 /* static */
   1251 bool SavedFrame::functionDisplayNameProperty(JSContext* cx, unsigned argc,
   1252                                             Value* vp) {
   1253  THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", args, frame);
   1254  JSPrincipals* principals = cx->realm()->principals();
   1255  RootedString name(cx);
   1256  JS::SavedFrameResult result =
   1257      JS::GetSavedFrameFunctionDisplayName(cx, principals, frame, &name);
   1258  if (result == JS::SavedFrameResult::Ok && name) {
   1259    if (!cx->compartment()->wrap(cx, &name)) {
   1260      return false;
   1261    }
   1262    args.rval().setString(name);
   1263  } else {
   1264    args.rval().setNull();
   1265  }
   1266  return true;
   1267 }
   1268 
   1269 /* static */
   1270 bool SavedFrame::asyncCauseProperty(JSContext* cx, unsigned argc, Value* vp) {
   1271  THIS_SAVEDFRAME(cx, argc, vp, "(get asyncCause)", args, frame);
   1272  JSPrincipals* principals = cx->realm()->principals();
   1273  RootedString asyncCause(cx);
   1274  JS::SavedFrameResult result =
   1275      JS::GetSavedFrameAsyncCause(cx, principals, frame, &asyncCause);
   1276  if (result == JS::SavedFrameResult::Ok && asyncCause) {
   1277    if (!cx->compartment()->wrap(cx, &asyncCause)) {
   1278      return false;
   1279    }
   1280    args.rval().setString(asyncCause);
   1281  } else {
   1282    args.rval().setNull();
   1283  }
   1284  return true;
   1285 }
   1286 
   1287 /* static */
   1288 bool SavedFrame::asyncParentProperty(JSContext* cx, unsigned argc, Value* vp) {
   1289  THIS_SAVEDFRAME(cx, argc, vp, "(get asyncParent)", args, frame);
   1290  JSPrincipals* principals = cx->realm()->principals();
   1291  RootedObject asyncParent(cx);
   1292  (void)JS::GetSavedFrameAsyncParent(cx, principals, frame, &asyncParent);
   1293  if (!cx->compartment()->wrap(cx, &asyncParent)) {
   1294    return false;
   1295  }
   1296  args.rval().setObjectOrNull(asyncParent);
   1297  return true;
   1298 }
   1299 
   1300 /* static */
   1301 bool SavedFrame::parentProperty(JSContext* cx, unsigned argc, Value* vp) {
   1302  THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", args, frame);
   1303  JSPrincipals* principals = cx->realm()->principals();
   1304  RootedObject parent(cx);
   1305  (void)JS::GetSavedFrameParent(cx, principals, frame, &parent);
   1306  if (!cx->compartment()->wrap(cx, &parent)) {
   1307    return false;
   1308  }
   1309  args.rval().setObjectOrNull(parent);
   1310  return true;
   1311 }
   1312 
   1313 /* static */
   1314 bool SavedFrame::toStringMethod(JSContext* cx, unsigned argc, Value* vp) {
   1315  THIS_SAVEDFRAME(cx, argc, vp, "toString", args, frame);
   1316  JSPrincipals* principals = cx->realm()->principals();
   1317  RootedString string(cx);
   1318  if (!JS::BuildStackString(cx, principals, frame, &string)) {
   1319    return false;
   1320  }
   1321  args.rval().setString(string);
   1322  return true;
   1323 }
   1324 
   1325 bool SavedStacks::saveCurrentStack(
   1326    JSContext* cx, MutableHandle<SavedFrame*> frame,
   1327    JS::StackCapture&& capture /* = JS::StackCapture(JS::AllFrames()) */,
   1328    HandleObject startAt /* nullptr */) {
   1329  MOZ_RELEASE_ASSERT(cx->realm());
   1330  MOZ_DIAGNOSTIC_ASSERT(&cx->realm()->savedStacks() == this);
   1331 
   1332  if (creatingSavedFrame || cx->isExceptionPending() || !cx->global() ||
   1333      !cx->global()->isStandardClassResolved(JSProto_Object)) {
   1334    frame.set(nullptr);
   1335    return true;
   1336  }
   1337 
   1338  AutoGeckoProfilerEntry labelFrame(cx, "js::SavedStacks::saveCurrentStack");
   1339  return insertFrames(cx, frame, std::move(capture), startAt);
   1340 }
   1341 
   1342 bool SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack,
   1343                                 HandleString asyncCause,
   1344                                 MutableHandle<SavedFrame*> adoptedStack,
   1345                                 const Maybe<size_t>& maxFrameCount) {
   1346  MOZ_RELEASE_ASSERT(cx->realm());
   1347  MOZ_DIAGNOSTIC_ASSERT(&cx->realm()->savedStacks() == this);
   1348 
   1349  Rooted<JSAtom*> asyncCauseAtom(cx, AtomizeString(cx, asyncCause));
   1350  if (!asyncCauseAtom) {
   1351    return false;
   1352  }
   1353 
   1354  Rooted<SavedFrame*> asyncStackObj(
   1355      cx, asyncStack->maybeUnwrapAs<js::SavedFrame>());
   1356  MOZ_RELEASE_ASSERT(asyncStackObj);
   1357  adoptedStack.set(asyncStackObj);
   1358 
   1359  if (!adoptAsyncStack(cx, adoptedStack, asyncCauseAtom, maxFrameCount)) {
   1360    return false;
   1361  }
   1362 
   1363  return true;
   1364 }
   1365 
   1366 void SavedStacks::traceWeak(JSTracer* trc) {
   1367  frames.traceWeak(trc);
   1368  pcLocationMap.traceWeak(trc);
   1369 }
   1370 
   1371 void SavedStacks::trace(JSTracer* trc) { pcLocationMap.trace(trc); }
   1372 
   1373 uint32_t SavedStacks::count() { return frames.count(); }
   1374 
   1375 void SavedStacks::clear() { frames.clear(); }
   1376 
   1377 size_t SavedStacks::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
   1378  return frames.shallowSizeOfExcludingThis(mallocSizeOf) +
   1379         pcLocationMap.shallowSizeOfExcludingThis(mallocSizeOf);
   1380 }
   1381 
   1382 // Given that we have captured a stack frame with the given principals and
   1383 // source, return true if the requested `StackCapture` has been satisfied and
   1384 // stack walking can halt. Return false otherwise (and stack walking and frame
   1385 // capturing should continue).
   1386 static inline bool captureIsSatisfied(JSContext* cx, JSPrincipals* principals,
   1387                                      const JSAtom* source,
   1388                                      JS::StackCapture& capture) {
   1389  class Matcher {
   1390    JSContext* cx_;
   1391    JSPrincipals* framePrincipals_;
   1392    const JSAtom* frameSource_;
   1393 
   1394   public:
   1395    Matcher(JSContext* cx, JSPrincipals* principals, const JSAtom* source)
   1396        : cx_(cx), framePrincipals_(principals), frameSource_(source) {}
   1397 
   1398    bool operator()(JS::FirstSubsumedFrame& target) {
   1399      auto subsumes = cx_->runtime()->securityCallbacks->subsumes;
   1400      return (!subsumes || subsumes(target.principals, framePrincipals_)) &&
   1401             (!target.ignoreSelfHosted ||
   1402              frameSource_ != cx_->names().self_hosted_);
   1403    }
   1404 
   1405    bool operator()(JS::MaxFrames& target) { return target.maxFrames == 1; }
   1406 
   1407    bool operator()(JS::AllFrames&) { return false; }
   1408  };
   1409 
   1410  Matcher m(cx, principals, source);
   1411  return capture.match(m);
   1412 }
   1413 
   1414 bool SavedStacks::insertFrames(JSContext* cx, MutableHandle<SavedFrame*> frame,
   1415                               JS::StackCapture&& capture,
   1416                               HandleObject startAtObj) {
   1417  MOZ_ASSERT_IF(startAtObj, startAtObj->isCallable());
   1418 
   1419  // In order to look up a cached SavedFrame object, we need to have its parent
   1420  // SavedFrame, which means we need to walk the stack from oldest frame to
   1421  // youngest. However, FrameIter walks the stack from youngest frame to
   1422  // oldest. The solution is to append stack frames to a vector as we walk the
   1423  // stack with FrameIter, and then do a second pass through that vector in
   1424  // reverse order after the traversal has completed and get or create the
   1425  // SavedFrame objects at that time.
   1426  //
   1427  // To avoid making many copies of FrameIter (whose copy constructor is
   1428  // relatively slow), we use a vector of `SavedFrame::Lookup` objects, which
   1429  // only contain the FrameIter data we need. The `SavedFrame::Lookup`
   1430  // objects are partially initialized with everything except their parent
   1431  // pointers on the first pass, and then we fill in the parent pointers as we
   1432  // return in the second pass.
   1433 
   1434  // Accumulate the vector of Lookup objects here, youngest to oldest.
   1435  Rooted<js::GCLookupVector> stackChain(cx, js::GCLookupVector(cx));
   1436 
   1437  // If we find a cached saved frame, then that supplies the parent of the
   1438  // frames we have placed in stackChain. If we walk the stack all the way
   1439  // to the end, this remains null.
   1440  Rooted<SavedFrame*> cachedParentFrame(cx, nullptr);
   1441 
   1442  // Choose the right frame iteration strategy to accomodate both
   1443  // evalInFramePrev links and the LiveSavedFrameCache. For background, see
   1444  // the LiveSavedFrameCache comments in Stack.h.
   1445  //
   1446  // If we're using the LiveSavedFrameCache, then don't handle evalInFramePrev
   1447  // links by skipping over the frames altogether; that violates the cache's
   1448  // assumptions. Instead, traverse the entire stack, but choose each
   1449  // SavedFrame's parent as directed by the evalInFramePrev link, if any.
   1450  //
   1451  // If we're not using the LiveSavedFrameCache, it's hard to recover the
   1452  // frame to which the evalInFramePrev link refers, so we just let FrameIter
   1453  // skip those frames. Then each SavedFrame's parent is simply the frame that
   1454  // follows it in the stackChain vector, even when it has an evalInFramePrev
   1455  // link.
   1456  FrameIter iter(cx, capture.is<JS::AllFrames>()
   1457                         ? FrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK
   1458                         : FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK);
   1459 
   1460  // Once we've seen one frame with its hasCachedSavedFrame bit set, all its
   1461  // parents (that can be cached) ought to have it set too.
   1462  DebugOnly<bool> seenCached = false;
   1463 
   1464  // If we are using evalInFramePrev links to adjust the parents of debugger
   1465  // eval frames, we have to ensure the target frame is cached in the current
   1466  // realm. (This might not happen by default if the target frame is
   1467  // rematerialized, or if there is an async parent between the debugger eval
   1468  // frame and the target frame.) To accomplish this, we keep track of eval
   1469  // targets and ensure that we don't stop before they have all been reached.
   1470  Vector<AbstractFramePtr, 4, TempAllocPolicy> unreachedEvalTargets(cx);
   1471 
   1472  Rooted<JSFunction*> startAt(cx, startAtObj && startAtObj->is<JSFunction>()
   1473                                      ? &startAtObj->as<JSFunction>()
   1474                                      : nullptr);
   1475  bool seenStartAt = !startAt;
   1476 
   1477  while (!iter.done()) {
   1478    Activation& activation = *iter.activation();
   1479    Maybe<LiveSavedFrameCache::FramePtr> framePtr =
   1480        LiveSavedFrameCache::FramePtr::create(iter);
   1481 
   1482    if (capture.is<JS::AllFrames>() && iter.hasUsableAbstractFramePtr()) {
   1483      unreachedEvalTargets.eraseIfEqual(iter.abstractFramePtr());
   1484    }
   1485 
   1486    if (framePtr) {
   1487      // In general, when we reach a frame with its hasCachedSavedFrame bit set,
   1488      // all its parents will have the bit set as well. See the
   1489      // LiveSavedFrameCache comment in Activation.h for more details. There are
   1490      // a few exceptions:
   1491      // - Rematerialized frames are always created with the bit clear.
   1492      // - Captures using FirstSubsumedFrame ignore async parents and walk the
   1493      //   real stack. Because we're using different rules for walking the
   1494      //   stack, we can reach frames that weren't cached in a previous
   1495      //   AllFrames traversal.
   1496      DebugOnly<bool> hasGoodExcuse = framePtr->isRematerializedFrame() ||
   1497                                      capture.is<JS::FirstSubsumedFrame>();
   1498      MOZ_ASSERT_IF(seenCached,
   1499                    framePtr->hasCachedSavedFrame() || hasGoodExcuse);
   1500      seenCached |= framePtr->hasCachedSavedFrame();
   1501 
   1502      if (capture.is<JS::AllFrames>() && framePtr->isInterpreterFrame() &&
   1503          framePtr->asInterpreterFrame().isDebuggerEvalFrame()) {
   1504        AbstractFramePtr target =
   1505            framePtr->asInterpreterFrame().evalInFramePrev();
   1506        if (!unreachedEvalTargets.append(target)) {
   1507          return false;
   1508        }
   1509      }
   1510    }
   1511 
   1512    if (capture.is<JS::AllFrames>() && framePtr &&
   1513        framePtr->hasCachedSavedFrame()) {
   1514      auto* cache = activation.getLiveSavedFrameCache(cx);
   1515      if (!cache) {
   1516        return false;
   1517      }
   1518      cache->find(cx, *framePtr, iter.pc(), &cachedParentFrame);
   1519 
   1520      // Even though iter.hasCachedSavedFrame() was true, we may still get a
   1521      // cache miss, if the frame's pc doesn't match the cache entry's, or if
   1522      // the cache was emptied due to a realm mismatch. If we got a cache hit,
   1523      // and we do not have to keep looking for unreached eval target frames,
   1524      // we can stop traversing the stack and start building the chain.
   1525      if (cachedParentFrame && unreachedEvalTargets.empty()) {
   1526        break;
   1527      }
   1528 
   1529      // This frame doesn't have a cache entry, despite its hasCachedSavedFrame
   1530      // flag being set. If this was due to a pc mismatch, we can clear the flag
   1531      // here and set things right. If the cache was emptied due to a realm
   1532      // mismatch, we should clear all the frames' flags as we walk to the
   1533      // bottom of the stack, so that they are all clear before we start pushing
   1534      // any new entries.
   1535      framePtr->clearHasCachedSavedFrame();
   1536    }
   1537 
   1538    // We'll be pushing this frame onto stackChain. Gather the information
   1539    // needed to construct the SavedFrame::Lookup.
   1540    Rooted<LocationValue> location(cx);
   1541    {
   1542      AutoRealmUnchecked ar(cx, iter.realm());
   1543      if (!cx->realm()->savedStacks().getLocation(cx, iter, &location)) {
   1544        return false;
   1545      }
   1546    }
   1547 
   1548    Rooted<JSAtom*> displayAtom(cx, iter.maybeFunctionDisplayAtom());
   1549 
   1550    auto principals = iter.realm()->principals();
   1551    MOZ_ASSERT_IF(framePtr && !iter.isWasm(), iter.pc());
   1552 
   1553    // If we haven't yet seen the start, then don't add anything to the stack
   1554    // chain.
   1555    if (seenStartAt) {
   1556      if (!stackChain.emplaceBack(location.source(), location.sourceId(),
   1557                                  location.line(), location.column(),
   1558                                  displayAtom,
   1559                                  nullptr,  // asyncCause
   1560                                  nullptr,  // parent (not known yet)
   1561                                  principals, iter.mutedErrors(), framePtr,
   1562                                  iter.pc(), &activation)) {
   1563        return false;
   1564      }
   1565    }
   1566 
   1567    if (captureIsSatisfied(cx, principals, location.source(), capture)) {
   1568      break;
   1569    }
   1570 
   1571    if (!seenStartAt && iter.isFunctionFrame() &&
   1572        iter.matchCallee(cx, startAt)) {
   1573      seenStartAt = true;
   1574    }
   1575 
   1576    ++iter;
   1577    framePtr = LiveSavedFrameCache::FramePtr::create(iter);
   1578 
   1579    if (iter.activation() != &activation && capture.is<JS::AllFrames>()) {
   1580      // If there were no cache hits in the entire activation, clear its
   1581      // cache so we'll be able to push new ones when we build the
   1582      // SavedFrame chain.
   1583      activation.clearLiveSavedFrameCache();
   1584    }
   1585 
   1586    // If we have crossed into a new activation, check whether the prior
   1587    // activation had an async parent set.
   1588    //
   1589    // If the async call was explicit (async function resumptions, most
   1590    // testing facilities), then the async parent stack has priority over
   1591    // any actual frames still on the JavaScript stack. If the async call
   1592    // was implicit (DOM CallbackObject::CallSetup calls), then the async
   1593    // parent stack is used only if there were no other frames on the
   1594    // stack.
   1595    //
   1596    // Captures using FirstSubsumedFrame expect us to ignore async parents.
   1597    bool hasAsyncStackToAdopt =
   1598        iter.activation() != &activation && activation.asyncStack() &&
   1599        (activation.asyncCallIsExplicit() || iter.done()) &&
   1600        !capture.is<JS::FirstSubsumedFrame>();
   1601 
   1602    // If we're censoring the stack for Error.captureStackTrace we also
   1603    // don't want to re-parent an empty stack trace, so make sure
   1604    // we actually saw a frame; stop walking the trace if we haven't
   1605    // seen anything.
   1606    if (hasAsyncStackToAdopt && stackChain.length() == 0) {
   1607      break;
   1608    }
   1609 
   1610    if (hasAsyncStackToAdopt) {
   1611      // Atomize the async cause string. There should only be a few
   1612      // different strings used.
   1613      const char* cause = activation.asyncCause();
   1614      Rooted<JSAtom*> causeAtom(cx, AtomizeUTF8Chars(cx, cause, strlen(cause)));
   1615      if (!causeAtom) {
   1616        return false;
   1617      }
   1618 
   1619      // Translate our capture into a frame count limit for
   1620      // adoptAsyncStack, which will impose further limits.
   1621      Maybe<size_t> maxFrames =
   1622          !capture.is<JS::MaxFrames>() ? Nothing()
   1623          : capture.as<JS::MaxFrames>().maxFrames == 0
   1624              ? Nothing()
   1625              : Some(capture.as<JS::MaxFrames>().maxFrames);
   1626 
   1627      // Clip the stack if needed, attach the async cause string to the
   1628      // top frame, and copy it into our compartment if necessary.
   1629      Rooted<SavedFrame*> asyncParent(cx, activation.asyncStack());
   1630      if (!adoptAsyncStack(cx, &asyncParent, causeAtom, maxFrames)) {
   1631        return false;
   1632      }
   1633      stackChain[stackChain.length() - 1].setParent(asyncParent);
   1634      if (!capture.is<JS::AllFrames>() || unreachedEvalTargets.empty()) {
   1635        // In the case of a JS::AllFrames capture, we will be populating the
   1636        // LiveSavedFrameCache in the second loop. In the case where there is
   1637        // a debugger eval frame on the stack, the second loop will use
   1638        // checkForEvalInFramePrev to skip from the eval frame to the "prev"
   1639        // frame and assert that when this happens, the "prev"
   1640        // frame is in the cache. In cases where there is an async stack
   1641        // activation between the debugger eval frame and the "prev" frame,
   1642        // breaking here would not populate the "prev" cache entry, causing
   1643        // checkForEvalInFramePrev to fail.
   1644        break;
   1645      }
   1646 
   1647      // At this point, we would normally stop walking the stack, but
   1648      // we're continuing because of an unreached eval target. If a
   1649      // previous capture stopped here, it's possible that this frame was
   1650      // already cached, but its non-async parent wasn't, which violates
   1651      // our `seenCached` invariant. By clearing `seenCached` here, we
   1652      // avoid spurious assertions. We continue to enforce the invariant
   1653      // for subsequent frames: if any frame above this is cached, then
   1654      // all of that frame's parents should also be cached.
   1655      seenCached = false;
   1656    }
   1657 
   1658    if (capture.is<JS::MaxFrames>()) {
   1659      capture.as<JS::MaxFrames>().maxFrames--;
   1660    }
   1661  }
   1662 
   1663  // Iterate through |stackChain| in reverse order and get or create the
   1664  // actual SavedFrame instances.
   1665  frame.set(cachedParentFrame);
   1666  for (size_t i = stackChain.length(); i != 0; i--) {
   1667    MutableHandle<SavedFrame::Lookup> lookup = stackChain[i - 1];
   1668    if (!lookup.parent()) {
   1669      // The frame may already have an async parent frame set explicitly
   1670      // on its activation.
   1671      lookup.setParent(frame);
   1672    }
   1673 
   1674    // If necessary, adjust the parent of a debugger eval frame to point to
   1675    // the frame in whose scope the eval occurs - if we're using
   1676    // LiveSavedFrameCache. Otherwise, we simply ask the FrameIter to follow
   1677    // evalInFramePrev links, so that the parent is always the last frame we
   1678    // created.
   1679    if (capture.is<JS::AllFrames>() && lookup.framePtr()) {
   1680      if (!checkForEvalInFramePrev(cx, lookup)) {
   1681        return false;
   1682      }
   1683    }
   1684 
   1685    frame.set(getOrCreateSavedFrame(cx, lookup));
   1686    if (!frame) {
   1687      return false;
   1688    }
   1689 
   1690    if (capture.is<JS::AllFrames>() && lookup.framePtr()) {
   1691      auto* cache = lookup.activation()->getLiveSavedFrameCache(cx);
   1692      if (!cache ||
   1693          !cache->insert(cx, *lookup.framePtr(), lookup.pc(), frame)) {
   1694        return false;
   1695      }
   1696    }
   1697  }
   1698 
   1699  return true;
   1700 }
   1701 
   1702 bool SavedStacks::adoptAsyncStack(JSContext* cx,
   1703                                  MutableHandle<SavedFrame*> asyncStack,
   1704                                  Handle<JSAtom*> asyncCause,
   1705                                  const Maybe<size_t>& maxFrameCount) {
   1706  MOZ_ASSERT(asyncStack);
   1707  MOZ_ASSERT(asyncCause);
   1708 
   1709  // If maxFrameCount is Nothing, the caller asked for an unlimited number of
   1710  // stack frames, but async stacks are not limited by the available stack
   1711  // memory, so we need to set an arbitrary limit when collecting them. We
   1712  // still don't enforce an upper limit if the caller requested more frames.
   1713  size_t maxFrames = maxFrameCount.valueOr(ASYNC_STACK_MAX_FRAME_COUNT);
   1714 
   1715  // Turn the chain of frames starting with asyncStack into a vector of Lookup
   1716  // objects in |stackChain|, youngest to oldest.
   1717  Rooted<js::GCLookupVector> stackChain(cx, js::GCLookupVector(cx));
   1718  SavedFrame* currentSavedFrame = asyncStack;
   1719  while (currentSavedFrame && stackChain.length() < maxFrames) {
   1720    if (!stackChain.emplaceBack(*currentSavedFrame)) {
   1721      ReportOutOfMemory(cx);
   1722      return false;
   1723    }
   1724 
   1725    currentSavedFrame = currentSavedFrame->getParent();
   1726  }
   1727 
   1728  // Attach the asyncCause to the youngest frame.
   1729  stackChain[0].setAsyncCause(asyncCause);
   1730 
   1731  // If we walked the entire stack, and it's in cx's realm, we don't
   1732  // need to rebuild the full chain again using the lookup objects - we can
   1733  // just use the existing chain. Only the asyncCause on the youngest frame
   1734  // needs to be changed.
   1735  if (currentSavedFrame == nullptr && asyncStack->realm() == cx->realm()) {
   1736    MutableHandle<SavedFrame::Lookup> lookup = stackChain[0];
   1737    lookup.setParent(asyncStack->getParent());
   1738    asyncStack.set(getOrCreateSavedFrame(cx, lookup));
   1739    return !!asyncStack;
   1740  }
   1741 
   1742  // If we captured the maximum number of frames and the caller requested no
   1743  // specific limit, we only return half of them. This means that if we do
   1744  // many subsequent captures with the same async stack, it's likely we can
   1745  // use the optimization above.
   1746  if (maxFrameCount.isNothing() && currentSavedFrame) {
   1747    stackChain.shrinkBy(ASYNC_STACK_MAX_FRAME_COUNT / 2);
   1748  }
   1749 
   1750  // Iterate through |stackChain| in reverse order and get or create the
   1751  // actual SavedFrame instances.
   1752  asyncStack.set(nullptr);
   1753  while (!stackChain.empty()) {
   1754    Rooted<SavedFrame::Lookup> lookup(cx, stackChain.back());
   1755    lookup.setParent(asyncStack);
   1756    asyncStack.set(getOrCreateSavedFrame(cx, lookup));
   1757    if (!asyncStack) {
   1758      return false;
   1759    }
   1760    stackChain.popBack();
   1761  }
   1762 
   1763  return true;
   1764 }
   1765 
   1766 // Given a |lookup| for which we're about to construct a SavedFrame, if it
   1767 // refers to a Debugger eval frame, adjust |lookup|'s parent to be the frame's
   1768 // evalInFramePrev target.
   1769 //
   1770 // Debugger eval frames run code in the scope of some random older frame on the
   1771 // stack (the 'target' frame). It is our custom to report the target as the
   1772 // immediate parent of the eval frame. The LiveSavedFrameCache requires us not
   1773 // to skip frames, so instead we walk the entire stack, and just give Debugger
   1774 // eval frames the right parents as we encounter them.
   1775 //
   1776 // Call this function only if we are using the LiveSavedFrameCache; otherwise,
   1777 // FrameIter has already taken care of getting us the right parent.
   1778 bool SavedStacks::checkForEvalInFramePrev(
   1779    JSContext* cx, MutableHandle<SavedFrame::Lookup> lookup) {
   1780  MOZ_ASSERT(lookup.framePtr());
   1781  if (!lookup.framePtr()->isInterpreterFrame()) {
   1782    return true;
   1783  }
   1784 
   1785  InterpreterFrame& interpreterFrame = lookup.framePtr()->asInterpreterFrame();
   1786  if (!interpreterFrame.isDebuggerEvalFrame()) {
   1787    return true;
   1788  }
   1789 
   1790  FrameIter iter(cx, FrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK);
   1791  while (!iter.done() &&
   1792         (!iter.hasUsableAbstractFramePtr() ||
   1793          iter.abstractFramePtr() != interpreterFrame.evalInFramePrev())) {
   1794    ++iter;
   1795  }
   1796 
   1797  Maybe<LiveSavedFrameCache::FramePtr> maybeTarget =
   1798      LiveSavedFrameCache::FramePtr::create(iter);
   1799  MOZ_ASSERT(maybeTarget);
   1800 
   1801  LiveSavedFrameCache::FramePtr target = *maybeTarget;
   1802 
   1803  // If we're caching the frame to which |lookup| refers, then we should
   1804  // definitely have the target frame in the cache as well.
   1805  MOZ_ASSERT(target.hasCachedSavedFrame());
   1806 
   1807  // Search the chain of activations for a LiveSavedFrameCache that has an
   1808  // entry for target.
   1809  Rooted<SavedFrame*> saved(cx, nullptr);
   1810  for (Activation* act = lookup.activation(); act; act = act->prev()) {
   1811    // It's okay to force allocation of a cache here; we're about to put
   1812    // something in the top cache, and all the lower ones should exist
   1813    // already.
   1814    auto* cache = act->getLiveSavedFrameCache(cx);
   1815    if (!cache) {
   1816      return false;
   1817    }
   1818 
   1819    cache->findWithoutInvalidation(target, &saved);
   1820    if (saved) {
   1821      break;
   1822    }
   1823  }
   1824 
   1825  // Since |target| has its cached bit set, we should have found it.
   1826  MOZ_ALWAYS_TRUE(saved);
   1827 
   1828  // Because we use findWithoutInvalidation here, we can technically get a
   1829  // SavedFrame here for any realm. That shouldn't happen here because
   1830  // checkForEvalInFramePrev is only called _after_ the parent frames have
   1831  // been constructed, but if something prevents the chain from being properly
   1832  // reconstructed, that invariant could be accidentally broken.
   1833  MOZ_ASSERT(saved->realm() == cx->realm());
   1834 
   1835  lookup.setParent(saved);
   1836  return true;
   1837 }
   1838 
   1839 SavedFrame* SavedStacks::getOrCreateSavedFrame(
   1840    JSContext* cx, Handle<SavedFrame::Lookup> lookup) {
   1841  const SavedFrame::Lookup& lookupInstance = lookup.get();
   1842  DependentAddPtr<SavedFrame::Set> p(cx, frames, lookupInstance);
   1843  if (p) {
   1844    MOZ_ASSERT(*p);
   1845    return *p;
   1846  }
   1847 
   1848  SavedFrame* frame = createFrameFromLookup(cx, lookup);
   1849  if (!frame) {
   1850    return nullptr;
   1851  }
   1852 
   1853  if (!p.add(cx, frames, lookupInstance, frame)) {
   1854    return nullptr;
   1855  }
   1856 
   1857  return frame;
   1858 }
   1859 
   1860 SavedFrame* SavedStacks::createFrameFromLookup(
   1861    JSContext* cx, Handle<SavedFrame::Lookup> lookup) {
   1862  Rooted<SavedFrame*> frame(cx, SavedFrame::create(cx));
   1863  if (!frame) {
   1864    return nullptr;
   1865  }
   1866  frame->initFromLookup(cx, lookup);
   1867 
   1868  if (!FreezeObject(cx, frame)) {
   1869    return nullptr;
   1870  }
   1871 
   1872  return frame;
   1873 }
   1874 
   1875 bool SavedStacks::getLocation(JSContext* cx, const FrameIter& iter,
   1876                              MutableHandle<LocationValue> locationp) {
   1877  // We should only ever be caching location values for scripts in this
   1878  // compartment. Otherwise, we would get dead cross-compartment scripts in
   1879  // the cache because our compartment's sweep method isn't called when their
   1880  // compartment gets collected.
   1881  MOZ_DIAGNOSTIC_ASSERT(&cx->realm()->savedStacks() == this);
   1882  cx->check(iter.compartment());
   1883 
   1884  // When we have a |JSScript| for this frame, use a potentially memoized
   1885  // location from our PCLocationMap and copy it into |locationp|. When we do
   1886  // not have a |JSScript| for this frame (wasm frames), we take a slow path
   1887  // that doesn't employ memoization, and update |locationp|'s slots directly.
   1888 
   1889  if (iter.isWasm()) {
   1890    // Only asm.js has a displayURL.
   1891    if (const char16_t* displayURL = iter.displayURL()) {
   1892      locationp.setSource(AtomizeChars(cx, displayURL, js_strlen(displayURL)));
   1893    } else {
   1894      const char* filename = iter.filename() ? iter.filename() : "";
   1895      locationp.setSource(AtomizeUTF8Chars(cx, filename, strlen(filename)));
   1896    }
   1897    if (!locationp.source()) {
   1898      return false;
   1899    }
   1900 
   1901    JS::TaggedColumnNumberOneOrigin column;
   1902    locationp.setLine(iter.computeLine(&column));
   1903    locationp.setColumn(column);
   1904    return true;
   1905  }
   1906 
   1907  RootedScript script(cx, iter.script());
   1908  jsbytecode* pc = iter.pc();
   1909 
   1910  PCLocationMap::AddPtr p = pcLocationMap.lookupForAdd(PCKey(script, pc));
   1911 
   1912  if (!p) {
   1913    Rooted<JSAtom*> source(cx);
   1914    if (const char16_t* displayURL = iter.displayURL()) {
   1915      source = AtomizeChars(cx, displayURL, js_strlen(displayURL));
   1916    } else {
   1917      const char* filename = script->filename() ? script->filename() : "";
   1918      source = AtomizeUTF8Chars(cx, filename, strlen(filename));
   1919    }
   1920    if (!source) {
   1921      return false;
   1922    }
   1923 
   1924    uint32_t sourceId = script->scriptSource()->id();
   1925    JS::LimitedColumnNumberOneOrigin column;
   1926    uint32_t line = PCToLineNumber(script, pc, &column);
   1927 
   1928    PCKey key(script, pc);
   1929    LocationValue value(source, sourceId, line,
   1930                        JS::TaggedColumnNumberOneOrigin(column));
   1931    if (!pcLocationMap.add(p, key, value)) {
   1932      ReportOutOfMemory(cx);
   1933      return false;
   1934    }
   1935  }
   1936 
   1937  locationp.set(p->value());
   1938  return true;
   1939 }
   1940 
   1941 void SavedStacks::chooseSamplingProbability(Realm* realm) {
   1942  {
   1943    JSRuntime* runtime = realm->runtimeFromMainThread();
   1944    if (runtime->recordAllocationCallback) {
   1945      // The runtime is tracking allocations across all realms, in this case
   1946      // ignore all of the debugger values, and use the runtime's probability.
   1947      this->setSamplingProbability(runtime->allocationSamplingProbability);
   1948      return;
   1949    }
   1950  }
   1951 
   1952  // Use unbarriered version to prevent triggering read barrier while
   1953  // collecting, this is safe as long as global does not escape.
   1954  GlobalObject* global = realm->unsafeUnbarrieredMaybeGlobal();
   1955  if (!global) {
   1956    return;
   1957  }
   1958 
   1959  Maybe<double> probability = DebugAPI::allocationSamplingProbability(global);
   1960  if (probability.isNothing()) {
   1961    return;
   1962  }
   1963 
   1964  this->setSamplingProbability(*probability);
   1965 }
   1966 
   1967 void SavedStacks::setSamplingProbability(double probability) {
   1968  if (!bernoulliSeeded) {
   1969    mozilla::Array<uint64_t, 2> seed;
   1970    GenerateXorShift128PlusSeed(seed);
   1971    bernoulli.setRandomState(seed[0], seed[1]);
   1972    bernoulliSeeded = true;
   1973  }
   1974 
   1975  bernoulli.setProbability(probability);
   1976 }
   1977 
   1978 JSObject* SavedStacks::MetadataBuilder::build(
   1979    JSContext* cx, HandleObject target,
   1980    AutoEnterOOMUnsafeRegion& oomUnsafe) const {
   1981  RootedObject obj(cx, target);
   1982 
   1983  SavedStacks& stacks = cx->realm()->savedStacks();
   1984  if (!stacks.bernoulli.trial()) {
   1985    return nullptr;
   1986  }
   1987 
   1988  Rooted<SavedFrame*> frame(cx);
   1989  if (!stacks.saveCurrentStack(cx, &frame)) {
   1990    oomUnsafe.crash("SavedStacksMetadataBuilder");
   1991  }
   1992 
   1993  if (!DebugAPI::onLogAllocationSite(cx, obj, frame,
   1994                                     mozilla::TimeStamp::Now())) {
   1995    oomUnsafe.crash("SavedStacksMetadataBuilder");
   1996  }
   1997 
   1998  auto recordAllocationCallback =
   1999      cx->realm()->runtimeFromMainThread()->recordAllocationCallback;
   2000  if (recordAllocationCallback) {
   2001    // The following code translates the JS-specific information, into an
   2002    // RecordAllocationInfo object that can be consumed outside of SpiderMonkey.
   2003 
   2004    auto node = JS::ubi::Node(obj.get());
   2005 
   2006    // Pass the non-SpiderMonkey specific information back to the
   2007    // callback to get it out of the JS engine.
   2008    recordAllocationCallback(JS::RecordAllocationInfo{
   2009        node.typeName(), node.jsObjectClassName(), node.descriptiveTypeName(),
   2010        JS::ubi::CoarseTypeToString(node.coarseType()),
   2011        node.size(cx->runtime()->debuggerMallocSizeOf),
   2012        gc::IsInsideNursery(obj)});
   2013  }
   2014 
   2015  MOZ_ASSERT_IF(frame, !frame->is<WrapperObject>());
   2016  return frame;
   2017 }
   2018 
   2019 const SavedStacks::MetadataBuilder SavedStacks::metadataBuilder;
   2020 
   2021 /* static */
   2022 constinit ReconstructedSavedFramePrincipals
   2023    ReconstructedSavedFramePrincipals::IsSystem;
   2024 /* static */
   2025 constinit ReconstructedSavedFramePrincipals
   2026    ReconstructedSavedFramePrincipals::IsNotSystem;
   2027 
   2028 UniqueChars BuildUTF8StackString(JSContext* cx, JSPrincipals* principals,
   2029                                 HandleObject stack) {
   2030  RootedString stackStr(cx);
   2031  if (!JS::BuildStackString(cx, principals, stack, &stackStr)) {
   2032    return nullptr;
   2033  }
   2034 
   2035  return JS_EncodeStringToUTF8(cx, stackStr);
   2036 }
   2037 
   2038 } /* namespace js */
   2039 
   2040 namespace JS {
   2041 namespace ubi {
   2042 
   2043 bool ConcreteStackFrame<SavedFrame>::isSystem() const {
   2044  auto trustedPrincipals = get().runtimeFromAnyThread()->trustedPrincipals();
   2045  return get().getPrincipals() == trustedPrincipals ||
   2046         get().getPrincipals() ==
   2047             &js::ReconstructedSavedFramePrincipals::IsSystem;
   2048 }
   2049 
   2050 bool ConcreteStackFrame<SavedFrame>::constructSavedFrameStack(
   2051    JSContext* cx, MutableHandleObject outSavedFrameStack) const {
   2052  outSavedFrameStack.set(&get());
   2053  if (!cx->compartment()->wrap(cx, outSavedFrameStack)) {
   2054    outSavedFrameStack.set(nullptr);
   2055    return false;
   2056  }
   2057  return true;
   2058 }
   2059 
   2060 // A `mozilla::Variant` matcher that converts the inner value of a
   2061 // `JS::ubi::AtomOrTwoByteChars` string to a `JSAtom*`.
   2062 struct MOZ_STACK_CLASS AtomizingMatcher {
   2063  JSContext* cx;
   2064  size_t length;
   2065 
   2066  explicit AtomizingMatcher(JSContext* cx, size_t length)
   2067      : cx(cx), length(length) {}
   2068 
   2069  JSAtom* operator()(JSAtom* atom) {
   2070    MOZ_ASSERT(atom);
   2071    return atom;
   2072  }
   2073 
   2074  JSAtom* operator()(const char16_t* chars) {
   2075    MOZ_ASSERT(chars);
   2076    return AtomizeChars(cx, chars, length);
   2077  }
   2078 };
   2079 
   2080 JS_PUBLIC_API bool ConstructSavedFrameStackSlow(
   2081    JSContext* cx, JS::ubi::StackFrame& frame,
   2082    MutableHandleObject outSavedFrameStack) {
   2083  Rooted<js::GCLookupVector> stackChain(cx, js::GCLookupVector(cx));
   2084  Rooted<JS::ubi::StackFrame> ubiFrame(cx, frame);
   2085 
   2086  while (ubiFrame.get()) {
   2087    // Convert the source and functionDisplayName strings to atoms.
   2088 
   2089    Rooted<JSAtom*> source(cx);
   2090    AtomizingMatcher atomizer(cx, ubiFrame.get().sourceLength());
   2091    source = ubiFrame.get().source().match(atomizer);
   2092    if (!source) {
   2093      return false;
   2094    }
   2095 
   2096    Rooted<JSAtom*> functionDisplayName(cx);
   2097    auto nameLength = ubiFrame.get().functionDisplayNameLength();
   2098    if (nameLength > 0) {
   2099      AtomizingMatcher atomizer(cx, nameLength);
   2100      functionDisplayName =
   2101          ubiFrame.get().functionDisplayName().match(atomizer);
   2102      if (!functionDisplayName) {
   2103        return false;
   2104      }
   2105    }
   2106 
   2107    auto principals =
   2108        js::ReconstructedSavedFramePrincipals::getSingleton(ubiFrame.get());
   2109 
   2110    if (!stackChain.emplaceBack(source, ubiFrame.get().sourceId(),
   2111                                ubiFrame.get().line(), ubiFrame.get().column(),
   2112                                functionDisplayName,
   2113                                /* asyncCause */ nullptr,
   2114                                /* parent */ nullptr, principals,
   2115                                /* mutedErrors */ true)) {
   2116      ReportOutOfMemory(cx);
   2117      return false;
   2118    }
   2119 
   2120    ubiFrame = ubiFrame.get().parent();
   2121  }
   2122 
   2123  Rooted<js::SavedFrame*> parentFrame(cx);
   2124  for (size_t i = stackChain.length(); i != 0; i--) {
   2125    MutableHandle<SavedFrame::Lookup> lookup = stackChain[i - 1];
   2126    lookup.setParent(parentFrame);
   2127    parentFrame = cx->realm()->savedStacks().getOrCreateSavedFrame(cx, lookup);
   2128    if (!parentFrame) {
   2129      return false;
   2130    }
   2131  }
   2132 
   2133  outSavedFrameStack.set(parentFrame);
   2134  return true;
   2135 }
   2136 
   2137 }  // namespace ubi
   2138 }  // namespace JS