LCOV - code coverage report
Current view: top level - js/src/vm - SavedStacks.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 432 846 51.1 %
Date: 2017-07-14 16:53:18 Functions: 58 95 61.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       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/ArrayUtils.h"
      10             : #include "mozilla/Attributes.h"
      11             : #include "mozilla/DebugOnly.h"
      12             : #include "mozilla/Move.h"
      13             : 
      14             : #include <algorithm>
      15             : #include <math.h>
      16             : 
      17             : #include "jsapi.h"
      18             : #include "jscompartment.h"
      19             : #include "jsfriendapi.h"
      20             : #include "jshashutil.h"
      21             : #include "jsmath.h"
      22             : #include "jsnum.h"
      23             : #include "jsscript.h"
      24             : 
      25             : #include "gc/Marking.h"
      26             : #include "gc/Policy.h"
      27             : #include "gc/Rooting.h"
      28             : #include "js/CharacterEncoding.h"
      29             : #include "js/Vector.h"
      30             : #include "vm/Debugger.h"
      31             : #include "vm/GeckoProfiler.h"
      32             : #include "vm/SavedFrame.h"
      33             : #include "vm/StringBuffer.h"
      34             : #include "vm/Time.h"
      35             : #include "vm/WrapperObject.h"
      36             : 
      37             : #include "jscntxtinlines.h"
      38             : 
      39             : #include "vm/NativeObject-inl.h"
      40             : #include "vm/Stack-inl.h"
      41             : 
      42             : using mozilla::AddToHash;
      43             : using mozilla::DebugOnly;
      44             : using mozilla::HashString;
      45             : using mozilla::Maybe;
      46             : using mozilla::Move;
      47             : using mozilla::Nothing;
      48             : using mozilla::Some;
      49             : 
      50             : namespace js {
      51             : 
      52             : /**
      53             :  * Maximum number of saved frames returned for an async stack.
      54             :  */
      55             : const uint32_t ASYNC_STACK_MAX_FRAME_COUNT = 60;
      56             : 
      57             : /* static */ Maybe<LiveSavedFrameCache::FramePtr>
      58       13980 : LiveSavedFrameCache::getFramePtr(FrameIter& iter)
      59             : {
      60       13980 :     if (iter.hasUsableAbstractFramePtr())
      61       13140 :         return Some(FramePtr(iter.abstractFramePtr()));
      62             : 
      63         840 :     if (iter.isPhysicalIonFrame())
      64         840 :         return Some(FramePtr(iter.physicalIonFrame()));
      65             : 
      66           0 :     return Nothing();
      67             : }
      68             : 
      69             : void
      70         106 : LiveSavedFrameCache::trace(JSTracer* trc)
      71             : {
      72         106 :     if (!initialized())
      73          74 :         return;
      74             : 
      75         243 :     for (auto* entry = frames->begin(); entry < frames->end(); entry++) {
      76         211 :         TraceEdge(trc,
      77             :                   &entry->savedFrame,
      78         211 :                   "LiveSavedFrameCache::frames SavedFrame");
      79             :     }
      80             : }
      81             : 
      82             : bool
      83        2641 : LiveSavedFrameCache::insert(JSContext* cx, FramePtr& framePtr, jsbytecode* pc,
      84             :                             HandleSavedFrame savedFrame)
      85             : {
      86        2641 :     MOZ_ASSERT(initialized());
      87             : 
      88        2641 :     if (!frames->emplaceBack(framePtr, pc, savedFrame)) {
      89           0 :         ReportOutOfMemory(cx);
      90           0 :         return false;
      91             :     }
      92             : 
      93             :     // Safe to dereference the cache key because the stack frames are still
      94             :     // live. After this point, they should never be dereferenced again.
      95        2641 :     if (framePtr.is<AbstractFramePtr>())
      96        2641 :         framePtr.as<AbstractFramePtr>().setHasCachedSavedFrame();
      97             :     else
      98           0 :         framePtr.as<jit::CommonFrameLayout*>()->setHasCachedSavedFrame();
      99             : 
     100        2641 :     return true;
     101             : }
     102             : 
     103             : void
     104        1517 : LiveSavedFrameCache::find(JSContext* cx, FrameIter& frameIter, MutableHandleSavedFrame frame) const
     105             : {
     106        1517 :     MOZ_ASSERT(initialized());
     107        1517 :     MOZ_ASSERT(!frameIter.done());
     108        1517 :     MOZ_ASSERT(frameIter.hasCachedSavedFrame());
     109             : 
     110        1861 :     Maybe<FramePtr> maybeFramePtr = getFramePtr(frameIter);
     111        1517 :     MOZ_ASSERT(maybeFramePtr.isSome());
     112             : 
     113        1861 :     FramePtr framePtr(*maybeFramePtr);
     114        1517 :     jsbytecode* pc = frameIter.pc();
     115        1517 :     size_t numberStillValid = 0;
     116             : 
     117        1517 :     frame.set(nullptr);
     118        2813 :     for (auto* p = frames->begin(); p < frames->end(); p++) {
     119        1640 :         numberStillValid++;
     120        1640 :         if (framePtr == p->framePtr && pc == p->pc) {
     121         344 :             frame.set(p->savedFrame);
     122         344 :             break;
     123             :         }
     124             :     }
     125             : 
     126        1517 :     if (!frame) {
     127        1173 :         frames->clear();
     128        1173 :         return;
     129             :     }
     130             : 
     131         344 :     MOZ_ASSERT(0 < numberStillValid && numberStillValid <= frames->length());
     132             : 
     133         344 :     if (frame->compartment() != cx->compartment()) {
     134         105 :         frame.set(nullptr);
     135         105 :         numberStillValid--;
     136             :     }
     137             : 
     138             :     // Everything after the cached SavedFrame are stale younger frames we have
     139             :     // since popped.
     140         344 :     frames->shrinkBy(frames->length() - numberStillValid);
     141             : }
     142             : 
     143       17457 : struct SavedFrame::Lookup {
     144       12463 :     Lookup(JSAtom* source, uint32_t line, uint32_t column,
     145             :            JSAtom* functionDisplayName, JSAtom* asyncCause, SavedFrame* parent,
     146             :            JSPrincipals* principals,
     147             :            const Maybe<LiveSavedFrameCache::FramePtr>& framePtr = Nothing(),
     148             :            jsbytecode* pc = nullptr, Activation* activation = nullptr)
     149       12463 :       : source(source),
     150             :         line(line),
     151             :         column(column),
     152             :         functionDisplayName(functionDisplayName),
     153             :         asyncCause(asyncCause),
     154             :         parent(parent),
     155             :         principals(principals),
     156             :         framePtr(framePtr),
     157             :         pc(pc),
     158       12463 :         activation(activation)
     159             :     {
     160       12463 :         MOZ_ASSERT(source);
     161       12463 :         MOZ_ASSERT_IF(framePtr.isSome(), activation);
     162       12463 :         MOZ_ASSERT_IF(framePtr.isSome() && !activation->isWasm(), pc);
     163             : 
     164             : #ifdef JS_MORE_DETERMINISTIC
     165             :         column = 0;
     166             : #endif
     167       12463 :     }
     168             : 
     169        2200 :     explicit Lookup(SavedFrame& savedFrame)
     170        2200 :       : source(savedFrame.getSource()),
     171        2200 :         line(savedFrame.getLine()),
     172        2200 :         column(savedFrame.getColumn()),
     173        2200 :         functionDisplayName(savedFrame.getFunctionDisplayName()),
     174        2200 :         asyncCause(savedFrame.getAsyncCause()),
     175        2200 :         parent(savedFrame.getParent()),
     176        2200 :         principals(savedFrame.getPrincipals()),
     177        2200 :         framePtr(Nothing()),
     178             :         pc(nullptr),
     179       17600 :         activation(nullptr)
     180             :     {
     181        2200 :         MOZ_ASSERT(source);
     182        2200 :     }
     183             : 
     184             :     JSAtom*       source;
     185             :     uint32_t      line;
     186             :     uint32_t      column;
     187             :     JSAtom*       functionDisplayName;
     188             :     JSAtom*       asyncCause;
     189             :     SavedFrame*   parent;
     190             :     JSPrincipals* principals;
     191             : 
     192             :     // These are used only by the LiveSavedFrameCache and not used for identity or
     193             :     // hashing.
     194             :     Maybe<LiveSavedFrameCache::FramePtr> framePtr;
     195             :     jsbytecode*                          pc;
     196             :     Activation*                          activation;
     197             : 
     198           0 :     void trace(JSTracer* trc) {
     199           0 :         TraceManuallyBarrieredEdge(trc, &source, "SavedFrame::Lookup::source");
     200           0 :         if (functionDisplayName) {
     201           0 :             TraceManuallyBarrieredEdge(trc, &functionDisplayName,
     202           0 :                                        "SavedFrame::Lookup::functionDisplayName");
     203             :         }
     204           0 :         if (asyncCause)
     205           0 :             TraceManuallyBarrieredEdge(trc, &asyncCause, "SavedFrame::Lookup::asyncCause");
     206           0 :         if (parent)
     207           0 :             TraceManuallyBarrieredEdge(trc, &parent, "SavedFrame::Lookup::parent");
     208           0 :     }
     209             : };
     210             : 
     211        3277 : class MOZ_STACK_CLASS SavedFrame::AutoLookupVector : public JS::CustomAutoRooter {
     212             :   public:
     213        3277 :     explicit AutoLookupVector(JSContext* cx)
     214        3277 :       : JS::CustomAutoRooter(cx),
     215        3277 :         lookups(cx)
     216        3277 :     { }
     217             : 
     218             :     typedef Vector<Lookup, ASYNC_STACK_MAX_FRAME_COUNT> LookupVector;
     219       18256 :     inline LookupVector* operator->() { return &lookups; }
     220       14053 :     inline HandleLookup operator[](size_t i) { return HandleLookup(lookups[i]); }
     221             : 
     222             :   private:
     223             :     LookupVector lookups;
     224             : 
     225           0 :     virtual void trace(JSTracer* trc) {
     226           0 :         for (size_t i = 0; i < lookups.length(); i++)
     227           0 :             lookups[i].trace(trc);
     228           0 :     }
     229             : };
     230             : 
     231             : /* static */ bool
     232           0 : SavedFrame::HashPolicy::hasHash(const Lookup& l)
     233             : {
     234           0 :     return SavedFramePtrHasher::hasHash(l.parent);
     235             : }
     236             : 
     237             : /* static */ bool
     238       14053 : SavedFrame::HashPolicy::ensureHash(const Lookup& l)
     239             : {
     240       14053 :     return SavedFramePtrHasher::ensureHash(l.parent);
     241             : }
     242             : 
     243             : /* static */ HashNumber
     244       16069 : SavedFrame::HashPolicy::hash(const Lookup& lookup)
     245             : {
     246       32138 :     JS::AutoCheckCannotGC nogc;
     247             :     // Assume that we can take line mod 2^32 without losing anything of
     248             :     // interest.  If that assumption changes, we'll just need to start with 0
     249             :     // and add another overload of AddToHash with more arguments.
     250       32138 :     return AddToHash(lookup.line,
     251       16069 :                      lookup.column,
     252       16069 :                      lookup.source,
     253       16069 :                      lookup.functionDisplayName,
     254       16069 :                      lookup.asyncCause,
     255             :                      SavedFramePtrHasher::hash(lookup.parent),
     256       32138 :                      JSPrincipalsPtrHasher::hash(lookup.principals));
     257             : }
     258             : 
     259             : /* static */ bool
     260       12037 : SavedFrame::HashPolicy::match(SavedFrame* existing, const Lookup& lookup)
     261             : {
     262       12037 :     MOZ_ASSERT(existing);
     263             : 
     264       12037 :     if (existing->getLine() != lookup.line)
     265           0 :         return false;
     266             : 
     267       12037 :     if (existing->getColumn() != lookup.column)
     268           0 :         return false;
     269             : 
     270       12037 :     if (existing->getParent() != lookup.parent)
     271           0 :         return false;
     272             : 
     273       12037 :     if (existing->getPrincipals() != lookup.principals)
     274           0 :         return false;
     275             : 
     276       12037 :     JSAtom* source = existing->getSource();
     277       12037 :     if (source != lookup.source)
     278           0 :         return false;
     279             : 
     280       12037 :     JSAtom* functionDisplayName = existing->getFunctionDisplayName();
     281       12037 :     if (functionDisplayName != lookup.functionDisplayName)
     282           0 :         return false;
     283             : 
     284       12037 :     JSAtom* asyncCause = existing->getAsyncCause();
     285       12037 :     if (asyncCause != lookup.asyncCause)
     286           0 :         return false;
     287             : 
     288       12037 :     return true;
     289             : }
     290             : 
     291             : /* static */ void
     292           0 : SavedFrame::HashPolicy::rekey(Key& key, const Key& newKey)
     293             : {
     294           0 :     key = newKey;
     295           0 : }
     296             : 
     297             : /* static */ bool
     298          62 : SavedFrame::finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject proto)
     299             : {
     300             :     // The only object with the SavedFrame::class_ that doesn't have a source
     301             :     // should be the prototype.
     302          62 :     proto->as<NativeObject>().setReservedSlot(SavedFrame::JSSLOT_SOURCE, NullValue());
     303             : 
     304          62 :     return FreezeObject(cx, proto);
     305             : }
     306             : 
     307             : static const ClassOps SavedFrameClassOps = {
     308             :     nullptr,                    // addProperty
     309             :     nullptr,                    // delProperty
     310             :     nullptr,                    // getProperty
     311             :     nullptr,                    // setProperty
     312             :     nullptr,                    // enumerate
     313             :     nullptr,                    // newEnumerate
     314             :     nullptr,                    // resolve
     315             :     nullptr,                    // mayResolve
     316             :     SavedFrame::finalize,       // finalize
     317             :     nullptr,                    // call
     318             :     nullptr,                    // hasInstance
     319             :     nullptr,                    // construct
     320             :     nullptr,                    // trace
     321             : };
     322             : 
     323             : const ClassSpec SavedFrame::classSpec_ = {
     324             :     GenericCreateConstructor<SavedFrame::construct, 0, gc::AllocKind::FUNCTION>,
     325             :     GenericCreatePrototype,
     326             :     SavedFrame::staticFunctions,
     327             :     nullptr,
     328             :     SavedFrame::protoFunctions,
     329             :     SavedFrame::protoAccessors,
     330             :     SavedFrame::finishSavedFrameInit,
     331             :     ClassSpec::DontDefineConstructor
     332             : };
     333             : 
     334             : /* static */ const Class SavedFrame::class_ = {
     335             :     "SavedFrame",
     336             :     JSCLASS_HAS_PRIVATE |
     337             :     JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT) |
     338             :     JSCLASS_HAS_CACHED_PROTO(JSProto_SavedFrame) |
     339             :     JSCLASS_IS_ANONYMOUS |
     340             :     JSCLASS_FOREGROUND_FINALIZE,
     341             :     &SavedFrameClassOps,
     342             :     &SavedFrame::classSpec_
     343             : };
     344             : 
     345             : /* static */ const JSFunctionSpec
     346             : SavedFrame::staticFunctions[] = {
     347             :     JS_FS_END
     348             : };
     349             : 
     350             : /* static */ const JSFunctionSpec
     351             : SavedFrame::protoFunctions[] = {
     352             :     JS_FN("constructor", SavedFrame::construct, 0, 0),
     353             :     JS_FN("toString", SavedFrame::toStringMethod, 0, 0),
     354             :     JS_FS_END
     355             : };
     356             : 
     357             : /* static */ const JSPropertySpec
     358             : SavedFrame::protoAccessors[] = {
     359             :     JS_PSG("source", SavedFrame::sourceProperty, 0),
     360             :     JS_PSG("line", SavedFrame::lineProperty, 0),
     361             :     JS_PSG("column", SavedFrame::columnProperty, 0),
     362             :     JS_PSG("functionDisplayName", SavedFrame::functionDisplayNameProperty, 0),
     363             :     JS_PSG("asyncCause", SavedFrame::asyncCauseProperty, 0),
     364             :     JS_PSG("asyncParent", SavedFrame::asyncParentProperty, 0),
     365             :     JS_PSG("parent", SavedFrame::parentProperty, 0),
     366             :     JS_PS_END
     367             : };
     368             : 
     369             : /* static */ void
     370           0 : SavedFrame::finalize(FreeOp* fop, JSObject* obj)
     371             : {
     372           0 :     MOZ_ASSERT(fop->onActiveCooperatingThread());
     373           0 :     JSPrincipals* p = obj->as<SavedFrame>().getPrincipals();
     374           0 :     if (p) {
     375           0 :         JSRuntime* rt = obj->runtimeFromActiveCooperatingThread();
     376           0 :         JS_DropPrincipals(rt->activeContextFromOwnThread(), p);
     377             :     }
     378           0 : }
     379             : 
     380             : JSAtom*
     381       14290 : SavedFrame::getSource()
     382             : {
     383       14290 :     const Value& v = getReservedSlot(JSSLOT_SOURCE);
     384       14290 :     JSString* s = v.toString();
     385       14290 :     return &s->asAtom();
     386             : }
     387             : 
     388             : uint32_t
     389       14254 : SavedFrame::getLine()
     390             : {
     391       14254 :     const Value& v = getReservedSlot(JSSLOT_LINE);
     392       14254 :     return v.toPrivateUint32();
     393             : }
     394             : 
     395             : uint32_t
     396       14238 : SavedFrame::getColumn()
     397             : {
     398       14238 :     const Value& v = getReservedSlot(JSSLOT_COLUMN);
     399       14238 :     return v.toPrivateUint32();
     400             : }
     401             : 
     402             : JSAtom*
     403       14238 : SavedFrame::getFunctionDisplayName()
     404             : {
     405       14238 :     const Value& v = getReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME);
     406       14238 :     if (v.isNull())
     407         334 :         return nullptr;
     408       13904 :     JSString* s = v.toString();
     409       13904 :     return &s->asAtom();
     410             : }
     411             : 
     412             : JSAtom*
     413       14238 : SavedFrame::getAsyncCause()
     414             : {
     415       14238 :     const Value& v = getReservedSlot(JSSLOT_ASYNCCAUSE);
     416       14238 :     if (v.isNull())
     417       13574 :         return nullptr;
     418         664 :     JSString* s = v.toString();
     419         664 :     return &s->asAtom();
     420             : }
     421             : 
     422             : SavedFrame*
     423       16437 : SavedFrame::getParent() const
     424             : {
     425       16437 :     const Value& v = getReservedSlot(JSSLOT_PARENT);
     426       16437 :     return v.isObject() ? &v.toObject().as<SavedFrame>() : nullptr;
     427             : }
     428             : 
     429             : JSPrincipals*
     430       14274 : SavedFrame::getPrincipals()
     431             : {
     432       14274 :     const Value& v = getReservedSlot(JSSLOT_PRINCIPALS);
     433       14274 :     if (v.isUndefined())
     434           0 :         return nullptr;
     435       14274 :     return static_cast<JSPrincipals*>(v.toPrivate());
     436             : }
     437             : 
     438             : void
     439        2016 : SavedFrame::initSource(JSAtom* source)
     440             : {
     441        2016 :     MOZ_ASSERT(source);
     442        2016 :     initReservedSlot(JSSLOT_SOURCE, StringValue(source));
     443        2016 : }
     444             : 
     445             : void
     446        2016 : SavedFrame::initLine(uint32_t line)
     447             : {
     448        2016 :     initReservedSlot(JSSLOT_LINE, PrivateUint32Value(line));
     449        2016 : }
     450             : 
     451             : void
     452        2016 : SavedFrame::initColumn(uint32_t column)
     453             : {
     454             : #ifdef JS_MORE_DETERMINISTIC
     455             :     column = 0;
     456             : #endif
     457        2016 :     initReservedSlot(JSSLOT_COLUMN, PrivateUint32Value(column));
     458        2016 : }
     459             : 
     460             : void
     461        2016 : SavedFrame::initPrincipals(JSPrincipals* principals)
     462             : {
     463        2016 :     if (principals)
     464        2016 :         JS_HoldPrincipals(principals);
     465        2016 :     initPrincipalsAlreadyHeld(principals);
     466        2016 : }
     467             : 
     468             : void
     469        2016 : SavedFrame::initPrincipalsAlreadyHeld(JSPrincipals* principals)
     470             : {
     471        2016 :     MOZ_ASSERT_IF(principals, principals->refcount > 0);
     472        2016 :     initReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(principals));
     473        2016 : }
     474             : 
     475             : void
     476        2016 : SavedFrame::initFunctionDisplayName(JSAtom* maybeName)
     477             : {
     478        2016 :     initReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME, maybeName ? StringValue(maybeName) : NullValue());
     479        2016 : }
     480             : 
     481             : void
     482        2016 : SavedFrame::initAsyncCause(JSAtom* maybeCause)
     483             : {
     484        2016 :     initReservedSlot(JSSLOT_ASYNCCAUSE, maybeCause ? StringValue(maybeCause) : NullValue());
     485        2016 : }
     486             : 
     487             : void
     488        2016 : SavedFrame::initParent(SavedFrame* maybeParent)
     489             : {
     490        2016 :     initReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(maybeParent));
     491        2016 : }
     492             : 
     493             : void
     494        2016 : SavedFrame::initFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup)
     495             : {
     496             :     // Make sure any atoms used in the lookup are marked in the current zone.
     497             :     // Normally we would try to keep these mark bits up to date around the
     498             :     // points where the context moves between compartments, but Lookups live on
     499             :     // the stack (where the atoms are kept alive regardless) and this is a
     500             :     // more convenient pinchpoint.
     501        2016 :     if (lookup->source)
     502        2016 :         cx->markAtom(lookup->source);
     503        2016 :     if (lookup->functionDisplayName)
     504        1942 :         cx->markAtom(lookup->functionDisplayName);
     505        2016 :     if (lookup->asyncCause)
     506         172 :         cx->markAtom(lookup->asyncCause);
     507             : 
     508        2016 :     initSource(lookup->source);
     509        2016 :     initLine(lookup->line);
     510        2016 :     initColumn(lookup->column);
     511        2016 :     initFunctionDisplayName(lookup->functionDisplayName);
     512        2016 :     initAsyncCause(lookup->asyncCause);
     513        2016 :     initParent(lookup->parent);
     514        2016 :     initPrincipals(lookup->principals);
     515        2016 : }
     516             : 
     517             : /* static */ SavedFrame*
     518        2016 : SavedFrame::create(JSContext* cx)
     519             : {
     520        4032 :     RootedGlobalObject global(cx, cx->global());
     521        2016 :     assertSameCompartment(cx, global);
     522             : 
     523             :     // Ensure that we don't try to capture the stack again in the
     524             :     // `SavedStacksMetadataBuilder` for this new SavedFrame object, and
     525             :     // accidentally cause O(n^2) behavior.
     526        4032 :     SavedStacks::AutoReentrancyGuard guard(cx->compartment()->savedStacks());
     527             : 
     528        4032 :     RootedNativeObject proto(cx, GlobalObject::getOrCreateSavedFramePrototype(cx, global));
     529        2016 :     if (!proto)
     530           0 :         return nullptr;
     531        2016 :     assertSameCompartment(cx, proto);
     532             : 
     533        4032 :     RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto,
     534        4032 :                                                       TenuredObject));
     535        2016 :     if (!frameObj)
     536           0 :         return nullptr;
     537             : 
     538        2016 :     return &frameObj->as<SavedFrame>();
     539             : }
     540             : 
     541             : bool
     542          36 : SavedFrame::isSelfHosted(JSContext* cx)
     543             : {
     544          36 :     JSAtom* source = getSource();
     545          36 :     return source == cx->names().selfHosted;
     546             : }
     547             : 
     548             : /* static */ bool
     549           0 : SavedFrame::construct(JSContext* cx, unsigned argc, Value* vp)
     550             : {
     551             :     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
     552           0 :                               "SavedFrame");
     553           0 :     return false;
     554             : }
     555             : 
     556             : static bool
     557          37 : SavedFrameSubsumedByCaller(JSContext* cx, HandleSavedFrame frame)
     558             : {
     559          37 :     auto subsumes = cx->runtime()->securityCallbacks->subsumes;
     560          37 :     if (!subsumes)
     561           0 :         return true;
     562             : 
     563          37 :     auto currentCompartmentPrincipals = cx->compartment()->principals();
     564          37 :     MOZ_ASSERT(!ReconstructedSavedFramePrincipals::is(currentCompartmentPrincipals));
     565             : 
     566          37 :     auto framePrincipals = frame->getPrincipals();
     567             : 
     568             :     // Handle SavedFrames that have been reconstructed from stacks in a heap
     569             :     // snapshot.
     570          37 :     if (framePrincipals == &ReconstructedSavedFramePrincipals::IsSystem)
     571           0 :         return cx->runningWithTrustedPrincipals();
     572          37 :     if (framePrincipals == &ReconstructedSavedFramePrincipals::IsNotSystem)
     573           0 :         return true;
     574             : 
     575          37 :     return subsumes(currentCompartmentPrincipals, framePrincipals);
     576             : }
     577             : 
     578             : // Return the first SavedFrame in the chain that starts with |frame| whose
     579             : // principals are subsumed by |principals|, according to |subsumes|. If there is
     580             : // no such frame, return nullptr. |skippedAsync| is set to true if any of the
     581             : // skipped frames had the |asyncCause| property set, otherwise it is explicitly
     582             : // set to false.
     583             : static SavedFrame*
     584          37 : GetFirstSubsumedFrame(JSContext* cx, HandleSavedFrame frame, JS::SavedFrameSelfHosted selfHosted,
     585             :                       bool& skippedAsync)
     586             : {
     587          37 :     skippedAsync = false;
     588             : 
     589          74 :     RootedSavedFrame rootedFrame(cx, frame);
     590          37 :     while (rootedFrame) {
     591         110 :         if ((selfHosted == JS::SavedFrameSelfHosted::Include ||
     592         111 :              !rootedFrame->isSelfHosted(cx)) &&
     593         148 :             SavedFrameSubsumedByCaller(cx, rootedFrame))
     594             :         {
     595          37 :             return rootedFrame;
     596             :         }
     597             : 
     598           0 :         if (rootedFrame->getAsyncCause())
     599           0 :             skippedAsync = true;
     600             : 
     601           0 :         rootedFrame = rootedFrame->getParent();
     602             :     }
     603             : 
     604           0 :     return nullptr;
     605             : }
     606             : 
     607             : JS_FRIEND_API(JSObject*)
     608           0 : GetFirstSubsumedSavedFrame(JSContext* cx, HandleObject savedFrame,
     609             :                            JS::SavedFrameSelfHosted selfHosted)
     610             : {
     611           0 :     if (!savedFrame)
     612           0 :         return nullptr;
     613             :     bool skippedAsync;
     614           0 :     RootedSavedFrame frame(cx, &savedFrame->as<SavedFrame>());
     615           0 :     return GetFirstSubsumedFrame(cx, frame, selfHosted, skippedAsync);
     616             : }
     617             : 
     618             : static MOZ_MUST_USE bool
     619           0 : SavedFrame_checkThis(JSContext* cx, CallArgs& args, const char* fnName,
     620             :                      MutableHandleObject frame)
     621             : {
     622           0 :     const Value& thisValue = args.thisv();
     623             : 
     624           0 :     if (!thisValue.isObject()) {
     625           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
     626           0 :                                   InformalValueTypeName(thisValue));
     627           0 :         return false;
     628             :     }
     629             : 
     630           0 :     JSObject* thisObject = CheckedUnwrap(&thisValue.toObject());
     631           0 :     if (!thisObject || !thisObject->is<SavedFrame>()) {
     632           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
     633           0 :                                   SavedFrame::class_.name, fnName,
     634           0 :                                   thisObject ? thisObject->getClass()->name : "object");
     635           0 :         return false;
     636             :     }
     637             : 
     638             :     // Check for SavedFrame.prototype, which has the same class as SavedFrame
     639             :     // instances, however doesn't actually represent a captured stack frame. It
     640             :     // is the only object that is<SavedFrame>() but doesn't have a source.
     641           0 :     if (!SavedFrame::isSavedFrameAndNotProto(*thisObject)) {
     642             :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
     643           0 :                                   SavedFrame::class_.name, fnName, "prototype object");
     644           0 :         return false;
     645             :     }
     646             : 
     647             :     // Now set "frame" to the actual object we were invoked in (which may be a
     648             :     // wrapper), not the unwrapped version.  Consumers will need to know what
     649             :     // that original object was, and will do principal checks as needed.
     650           0 :     frame.set(&thisValue.toObject());
     651           0 :     return true;
     652             : }
     653             : 
     654             : // Get the SavedFrame * from the current this value and handle any errors that
     655             : // might occur therein.
     656             : //
     657             : // These parameters must already exist when calling this macro:
     658             : //   - JSContext* cx
     659             : //   - unsigned   argc
     660             : //   - Value*     vp
     661             : //   - const char* fnName
     662             : // These parameters will be defined after calling this macro:
     663             : //   - CallArgs args
     664             : //   - Rooted<SavedFrame*> frame (will be non-null)
     665             : #define THIS_SAVEDFRAME(cx, argc, vp, fnName, args, frame)             \
     666             :     CallArgs args = CallArgsFromVp(argc, vp);                          \
     667             :     RootedObject frame(cx);                                            \
     668             :     if (!SavedFrame_checkThis(cx, args, fnName, &frame))               \
     669             :         return false;
     670             : 
     671             : } /* namespace js */
     672             : 
     673             : namespace JS {
     674             : 
     675             : namespace {
     676             : 
     677             : // It's possible that our caller is privileged (and hence would see the entire
     678             : // stack) but we're working with an SavedFrame object that was captured in
     679             : // unprivileged code.  If so, drop privileges down to its level.  The idea is
     680             : // that this way devtools code that's asking an exception object for a stack to
     681             : // display will end up with the stack the web developer would see via doing
     682             : // .stack in a web page, with Firefox implementation details excluded.
     683             : //
     684             : // We want callers to pass us the object they were actually passed, not an
     685             : // unwrapped form of it.  That way Xray access to SavedFrame objects should not
     686             : // be affected by AutoMaybeEnterFrameCompartment and the only things that will
     687             : // be affected will be cases in which privileged code works with some C++ object
     688             : // that then pokes at an unprivileged StackFrame it has on hand.
     689          37 : class MOZ_STACK_CLASS AutoMaybeEnterFrameCompartment
     690             : {
     691             : public:
     692          37 :     AutoMaybeEnterFrameCompartment(JSContext* cx,
     693             :                                    HandleObject obj
     694             :                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     695          37 :     {
     696          37 :         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     697             : 
     698          37 :         MOZ_RELEASE_ASSERT(cx->compartment());
     699          37 :         if (obj)
     700          37 :             MOZ_RELEASE_ASSERT(obj->compartment());
     701             : 
     702             :         // Note that obj might be null here, since we're doing this before
     703             :         // UnwrapSavedFrame.
     704          37 :         if (obj && cx->compartment() != obj->compartment())
     705             :         {
     706           0 :             JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
     707           0 :             if (subsumes && subsumes(cx->compartment()->principals(),
     708           0 :                                      obj->compartment()->principals()))
     709             :             {
     710           0 :                 ac_.emplace(cx, obj);
     711             :             }
     712             :         }
     713          37 :     }
     714             : 
     715             :  private:
     716             :     Maybe<JSAutoCompartment> ac_;
     717             :     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     718             : };
     719             : 
     720             : } // namespace
     721             : 
     722             : static inline js::SavedFrame*
     723          37 : UnwrapSavedFrame(JSContext* cx, HandleObject obj, SavedFrameSelfHosted selfHosted,
     724             :                  bool& skippedAsync)
     725             : {
     726          37 :     if (!obj)
     727           0 :         return nullptr;
     728             : 
     729          74 :     RootedObject savedFrameObj(cx, CheckedUnwrap(obj));
     730          37 :     if (!savedFrameObj)
     731           0 :         return nullptr;
     732             : 
     733          37 :     MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
     734          74 :     js::RootedSavedFrame frame(cx, &savedFrameObj->as<js::SavedFrame>());
     735          37 :     return GetFirstSubsumedFrame(cx, frame, selfHosted, skippedAsync);
     736             : }
     737             : 
     738             : JS_PUBLIC_API(SavedFrameResult)
     739          17 : GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep,
     740             :                     SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
     741             : {
     742          17 :     js::AssertHeapIsIdle();
     743          34 :     CHECK_REQUEST(cx);
     744          17 :     MOZ_RELEASE_ASSERT(cx->compartment());
     745             : 
     746             :     {
     747          34 :         AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     748             :         bool skippedAsync;
     749          34 :         js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     750          17 :         if (!frame) {
     751           0 :             sourcep.set(cx->runtime()->emptyString);
     752           0 :             return SavedFrameResult::AccessDenied;
     753             :         }
     754          17 :         sourcep.set(frame->getSource());
     755             :     }
     756          17 :     if (sourcep->isAtom())
     757          17 :         cx->markAtom(&sourcep->asAtom());
     758          17 :     return SavedFrameResult::Ok;
     759             : }
     760             : 
     761             : JS_PUBLIC_API(SavedFrameResult)
     762          17 : GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep,
     763             :                   SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
     764             : {
     765          17 :     js::AssertHeapIsIdle();
     766          34 :     CHECK_REQUEST(cx);
     767          17 :     MOZ_RELEASE_ASSERT(cx->compartment());
     768          17 :     MOZ_ASSERT(linep);
     769             : 
     770          34 :     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     771             :     bool skippedAsync;
     772          34 :     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     773          17 :     if (!frame) {
     774           0 :         *linep = 0;
     775           0 :         return SavedFrameResult::AccessDenied;
     776             :     }
     777          17 :     *linep = frame->getLine();
     778          17 :     return SavedFrameResult::Ok;
     779             : }
     780             : 
     781             : JS_PUBLIC_API(SavedFrameResult)
     782           1 : GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp,
     783             :                     SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
     784             : {
     785           1 :     js::AssertHeapIsIdle();
     786           2 :     CHECK_REQUEST(cx);
     787           1 :     MOZ_RELEASE_ASSERT(cx->compartment());
     788           1 :     MOZ_ASSERT(columnp);
     789             : 
     790           2 :     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     791             :     bool skippedAsync;
     792           2 :     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     793           1 :     if (!frame) {
     794           0 :         *columnp = 0;
     795           0 :         return SavedFrameResult::AccessDenied;
     796             :     }
     797           1 :     *columnp = frame->getColumn();
     798           1 :     return SavedFrameResult::Ok;
     799             : }
     800             : 
     801             : JS_PUBLIC_API(SavedFrameResult)
     802           1 : GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep,
     803             :                                  SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
     804             : {
     805           1 :     js::AssertHeapIsIdle();
     806           2 :     CHECK_REQUEST(cx);
     807           1 :     MOZ_RELEASE_ASSERT(cx->compartment());
     808             : 
     809             :     {
     810           2 :         AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     811             :         bool skippedAsync;
     812           2 :         js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     813           1 :         if (!frame) {
     814           0 :             namep.set(nullptr);
     815           0 :             return SavedFrameResult::AccessDenied;
     816             :         }
     817           1 :         namep.set(frame->getFunctionDisplayName());
     818             :     }
     819           1 :     if (namep && namep->isAtom())
     820           1 :         cx->markAtom(&namep->asAtom());
     821           1 :     return SavedFrameResult::Ok;
     822             : }
     823             : 
     824             : JS_PUBLIC_API(SavedFrameResult)
     825           1 : GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep,
     826             :                         SavedFrameSelfHosted unused_ /* = SavedFrameSelfHosted::Include */)
     827             : {
     828           1 :     js::AssertHeapIsIdle();
     829           2 :     CHECK_REQUEST(cx);
     830           1 :     MOZ_RELEASE_ASSERT(cx->compartment());
     831             : 
     832             :     {
     833           2 :         AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     834             :         bool skippedAsync;
     835             :         // This function is always called with self-hosted frames excluded by
     836             :         // GetValueIfNotCached in dom/bindings/Exceptions.cpp. However, we want
     837             :         // to include them because our Promise implementation causes us to have
     838             :         // the async cause on a self-hosted frame. So we just ignore the
     839             :         // parameter and always include self-hosted frames.
     840           2 :         js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, SavedFrameSelfHosted::Include,
     841           2 :                                                         skippedAsync));
     842           1 :         if (!frame) {
     843           0 :             asyncCausep.set(nullptr);
     844           0 :             return SavedFrameResult::AccessDenied;
     845             :         }
     846           1 :         asyncCausep.set(frame->getAsyncCause());
     847           1 :         if (!asyncCausep && skippedAsync)
     848           0 :             asyncCausep.set(cx->names().Async);
     849             :     }
     850           1 :     if (asyncCausep && asyncCausep->isAtom())
     851           0 :         cx->markAtom(&asyncCausep->asAtom());
     852           1 :     return SavedFrameResult::Ok;
     853             : }
     854             : 
     855             : JS_PUBLIC_API(SavedFrameResult)
     856           0 : GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp,
     857             :                          SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
     858             : {
     859           0 :     js::AssertHeapIsIdle();
     860           0 :     CHECK_REQUEST(cx);
     861           0 :     MOZ_RELEASE_ASSERT(cx->compartment());
     862             : 
     863           0 :     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     864             :     bool skippedAsync;
     865           0 :     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     866           0 :     if (!frame) {
     867           0 :         asyncParentp.set(nullptr);
     868           0 :         return SavedFrameResult::AccessDenied;
     869             :     }
     870           0 :     js::RootedSavedFrame parent(cx, frame->getParent());
     871             : 
     872             :     // The current value of |skippedAsync| is not interesting, because we are
     873             :     // interested in whether we would cross any async parents to get from here
     874             :     // to the first subsumed parent frame instead.
     875           0 :     js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, selfHosted,
     876           0 :                                                                   skippedAsync));
     877             : 
     878             :     // Even if |parent| is not subsumed, we still want to return a pointer to it
     879             :     // rather than |subsumedParent| so it can pick up any |asyncCause| from the
     880             :     // inaccessible part of the chain.
     881           0 :     if (subsumedParent && (subsumedParent->getAsyncCause() || skippedAsync))
     882           0 :         asyncParentp.set(parent);
     883             :     else
     884           0 :         asyncParentp.set(nullptr);
     885           0 :     return SavedFrameResult::Ok;
     886             : }
     887             : 
     888             : JS_PUBLIC_API(SavedFrameResult)
     889           0 : GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp,
     890             :                     SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
     891             : {
     892           0 :     js::AssertHeapIsIdle();
     893           0 :     CHECK_REQUEST(cx);
     894           0 :     MOZ_RELEASE_ASSERT(cx->compartment());
     895             : 
     896           0 :     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     897             :     bool skippedAsync;
     898           0 :     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     899           0 :     if (!frame) {
     900           0 :         parentp.set(nullptr);
     901           0 :         return SavedFrameResult::AccessDenied;
     902             :     }
     903           0 :     js::RootedSavedFrame parent(cx, frame->getParent());
     904             : 
     905             :     // The current value of |skippedAsync| is not interesting, because we are
     906             :     // interested in whether we would cross any async parents to get from here
     907             :     // to the first subsumed parent frame instead.
     908           0 :     js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, selfHosted,
     909           0 :                                                                   skippedAsync));
     910             : 
     911             :     // Even if |parent| is not subsumed, we still want to return a pointer to it
     912             :     // rather than |subsumedParent| so it can pick up any |asyncCause| from the
     913             :     // inaccessible part of the chain.
     914           0 :     if (subsumedParent && !(subsumedParent->getAsyncCause() || skippedAsync))
     915           0 :         parentp.set(parent);
     916             :     else
     917           0 :         parentp.set(nullptr);
     918           0 :     return SavedFrameResult::Ok;
     919             : }
     920             : 
     921             : static bool
     922           0 : FormatSpiderMonkeyStackFrame(JSContext* cx, js::StringBuffer& sb,
     923             :                              js::HandleSavedFrame frame, size_t indent,
     924             :                              bool skippedAsync)
     925             : {
     926           0 :     RootedString asyncCause(cx, frame->getAsyncCause());
     927           0 :     if (!asyncCause && skippedAsync)
     928           0 :         asyncCause.set(cx->names().Async);
     929             : 
     930           0 :     js::RootedAtom name(cx, frame->getFunctionDisplayName());
     931           0 :     return (!indent || sb.appendN(' ', indent))
     932           0 :         && (!asyncCause || (sb.append(asyncCause) && sb.append('*')))
     933           0 :         && (!name || sb.append(name))
     934           0 :         && sb.append('@')
     935           0 :         && sb.append(frame->getSource())
     936           0 :         && sb.append(':')
     937           0 :         && NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
     938           0 :         && sb.append(':')
     939           0 :         && NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
     940           0 :         && sb.append('\n');
     941             : }
     942             : 
     943             : static bool
     944           0 : FormatV8StackFrame(JSContext* cx, js::StringBuffer& sb,
     945             :                    js::HandleSavedFrame frame, size_t indent, bool lastFrame)
     946             : {
     947           0 :     js::RootedAtom name(cx, frame->getFunctionDisplayName());
     948           0 :     return sb.appendN(' ', indent + 4)
     949           0 :         && sb.append('a')
     950           0 :         && sb.append('t')
     951           0 :         && sb.append(' ')
     952           0 :         && (!name || (sb.append(name) &&
     953           0 :                       sb.append(' ') &&
     954           0 :                       sb.append('(')))
     955           0 :         && sb.append(frame->getSource())
     956           0 :         && sb.append(':')
     957           0 :         && NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
     958           0 :         && sb.append(':')
     959           0 :         && NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
     960           0 :         && (!name || sb.append(')'))
     961           0 :         && (lastFrame || sb.append('\n'));
     962             : }
     963             : 
     964             : JS_PUBLIC_API(bool)
     965           0 : BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp,
     966             :                  size_t indent, js::StackFormat format)
     967             : {
     968           0 :     js::AssertHeapIsIdle();
     969           0 :     CHECK_REQUEST(cx);
     970           0 :     MOZ_RELEASE_ASSERT(cx->compartment());
     971             : 
     972           0 :     js::StringBuffer sb(cx);
     973             : 
     974           0 :     if (format == js::StackFormat::Default)
     975           0 :         format = cx->runtime()->stackFormat();
     976           0 :     MOZ_ASSERT(format != js::StackFormat::Default);
     977             : 
     978             :     // Enter a new block to constrain the scope of possibly entering the stack's
     979             :     // compartment. This ensures that when we finish the StringBuffer, we are
     980             :     // back in the cx's original compartment, and fulfill our contract with
     981             :     // callers to place the output string in the cx's current compartment.
     982             :     {
     983           0 :         AutoMaybeEnterFrameCompartment ac(cx, stack);
     984             :         bool skippedAsync;
     985           0 :         js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack, SavedFrameSelfHosted::Exclude,
     986           0 :                                                         skippedAsync));
     987           0 :         if (!frame) {
     988           0 :             stringp.set(cx->runtime()->emptyString);
     989           0 :             return true;
     990             :         }
     991             : 
     992           0 :         js::RootedSavedFrame parent(cx);
     993           0 :         do {
     994           0 :             MOZ_ASSERT(SavedFrameSubsumedByCaller(cx, frame));
     995           0 :             MOZ_ASSERT(!frame->isSelfHosted(cx));
     996             : 
     997           0 :             parent = frame->getParent();
     998             :             bool skippedNextAsync;
     999           0 :             js::RootedSavedFrame nextFrame(cx, js::GetFirstSubsumedFrame(cx, parent,
    1000           0 :                                                                          SavedFrameSelfHosted::Exclude, skippedNextAsync));
    1001             : 
    1002           0 :             switch (format) {
    1003             :                 case js::StackFormat::SpiderMonkey:
    1004           0 :                     if (!FormatSpiderMonkeyStackFrame(cx, sb, frame, indent, skippedAsync))
    1005           0 :                         return false;
    1006           0 :                     break;
    1007             :                 case js::StackFormat::V8:
    1008           0 :                     if (!FormatV8StackFrame(cx, sb, frame, indent, !nextFrame))
    1009           0 :                         return false;
    1010           0 :                     break;
    1011             :                 case js::StackFormat::Default:
    1012           0 :                     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected value");
    1013             :                     break;
    1014             :             }
    1015             : 
    1016           0 :             frame = nextFrame;
    1017           0 :             skippedAsync = skippedNextAsync;
    1018           0 :         } while (frame);
    1019             :     }
    1020             : 
    1021           0 :     JSString* str = sb.finishString();
    1022           0 :     if (!str)
    1023           0 :         return false;
    1024           0 :     assertSameCompartment(cx, str);
    1025           0 :     stringp.set(str);
    1026           0 :     return true;
    1027             : }
    1028             : 
    1029             : JS_PUBLIC_API(bool)
    1030           0 : IsSavedFrame(JSObject* obj)
    1031             : {
    1032           0 :     if (!obj)
    1033           0 :         return false;
    1034             : 
    1035           0 :     JSObject* unwrapped = js::CheckedUnwrap(obj);
    1036           0 :     if (!unwrapped)
    1037           0 :         return false;
    1038             : 
    1039           0 :     return js::SavedFrame::isSavedFrameAndNotProto(*unwrapped);
    1040             : }
    1041             : 
    1042             : } /* namespace JS */
    1043             : 
    1044             : namespace js {
    1045             : 
    1046             : /* static */ bool
    1047           0 : SavedFrame::sourceProperty(JSContext* cx, unsigned argc, Value* vp)
    1048             : {
    1049           0 :     THIS_SAVEDFRAME(cx, argc, vp, "(get source)", args, frame);
    1050           0 :     RootedString source(cx);
    1051           0 :     if (JS::GetSavedFrameSource(cx, frame, &source) == JS::SavedFrameResult::Ok) {
    1052           0 :         if (!cx->compartment()->wrap(cx, &source))
    1053           0 :             return false;
    1054           0 :         args.rval().setString(source);
    1055             :     } else {
    1056           0 :         args.rval().setNull();
    1057             :     }
    1058           0 :     return true;
    1059             : }
    1060             : 
    1061             : /* static */ bool
    1062           0 : SavedFrame::lineProperty(JSContext* cx, unsigned argc, Value* vp)
    1063             : {
    1064           0 :     THIS_SAVEDFRAME(cx, argc, vp, "(get line)", args, frame);
    1065             :     uint32_t line;
    1066           0 :     if (JS::GetSavedFrameLine(cx, frame, &line) == JS::SavedFrameResult::Ok)
    1067           0 :         args.rval().setNumber(line);
    1068             :     else
    1069           0 :         args.rval().setNull();
    1070           0 :     return true;
    1071             : }
    1072             : 
    1073             : /* static */ bool
    1074           0 : SavedFrame::columnProperty(JSContext* cx, unsigned argc, Value* vp)
    1075             : {
    1076           0 :     THIS_SAVEDFRAME(cx, argc, vp, "(get column)", args, frame);
    1077             :     uint32_t column;
    1078           0 :     if (JS::GetSavedFrameColumn(cx, frame, &column) == JS::SavedFrameResult::Ok)
    1079           0 :         args.rval().setNumber(column);
    1080             :     else
    1081           0 :         args.rval().setNull();
    1082           0 :     return true;
    1083             : }
    1084             : 
    1085             : /* static */ bool
    1086           0 : SavedFrame::functionDisplayNameProperty(JSContext* cx, unsigned argc, Value* vp)
    1087             : {
    1088           0 :     THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", args, frame);
    1089           0 :     RootedString name(cx);
    1090           0 :     JS::SavedFrameResult result = JS::GetSavedFrameFunctionDisplayName(cx, frame, &name);
    1091           0 :     if (result == JS::SavedFrameResult::Ok && name) {
    1092           0 :         if (!cx->compartment()->wrap(cx, &name))
    1093           0 :             return false;
    1094           0 :         args.rval().setString(name);
    1095             :     } else {
    1096           0 :         args.rval().setNull();
    1097             :     }
    1098           0 :     return true;
    1099             : }
    1100             : 
    1101             : /* static */ bool
    1102           0 : SavedFrame::asyncCauseProperty(JSContext* cx, unsigned argc, Value* vp)
    1103             : {
    1104           0 :     THIS_SAVEDFRAME(cx, argc, vp, "(get asyncCause)", args, frame);
    1105           0 :     RootedString asyncCause(cx);
    1106           0 :     JS::SavedFrameResult result = JS::GetSavedFrameAsyncCause(cx, frame, &asyncCause);
    1107           0 :     if (result == JS::SavedFrameResult::Ok && asyncCause) {
    1108           0 :         if (!cx->compartment()->wrap(cx, &asyncCause))
    1109           0 :             return false;
    1110           0 :         args.rval().setString(asyncCause);
    1111             :     } else {
    1112           0 :         args.rval().setNull();
    1113             :     }
    1114           0 :     return true;
    1115             : }
    1116             : 
    1117             : /* static */ bool
    1118           0 : SavedFrame::asyncParentProperty(JSContext* cx, unsigned argc, Value* vp)
    1119             : {
    1120           0 :     THIS_SAVEDFRAME(cx, argc, vp, "(get asyncParent)", args, frame);
    1121           0 :     RootedObject asyncParent(cx);
    1122           0 :     (void) JS::GetSavedFrameAsyncParent(cx, frame, &asyncParent);
    1123           0 :     if (!cx->compartment()->wrap(cx, &asyncParent))
    1124           0 :         return false;
    1125           0 :     args.rval().setObjectOrNull(asyncParent);
    1126           0 :     return true;
    1127             : }
    1128             : 
    1129             : /* static */ bool
    1130           0 : SavedFrame::parentProperty(JSContext* cx, unsigned argc, Value* vp)
    1131             : {
    1132           0 :     THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", args, frame);
    1133           0 :     RootedObject parent(cx);
    1134           0 :     (void) JS::GetSavedFrameParent(cx, frame, &parent);
    1135           0 :     if (!cx->compartment()->wrap(cx, &parent))
    1136           0 :         return false;
    1137           0 :     args.rval().setObjectOrNull(parent);
    1138           0 :     return true;
    1139             : }
    1140             : 
    1141             : /* static */ bool
    1142           0 : SavedFrame::toStringMethod(JSContext* cx, unsigned argc, Value* vp)
    1143             : {
    1144           0 :     THIS_SAVEDFRAME(cx, argc, vp, "toString", args, frame);
    1145           0 :     RootedString string(cx);
    1146           0 :     if (!JS::BuildStackString(cx, frame, &string))
    1147           0 :         return false;
    1148           0 :     args.rval().setString(string);
    1149           0 :     return true;
    1150             : }
    1151             : 
    1152             : bool
    1153         315 : SavedStacks::init()
    1154             : {
    1155         630 :     return frames.init() &&
    1156         630 :            pcLocationMap.init();
    1157             : }
    1158             : 
    1159             : bool
    1160        2961 : SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame,
    1161             :                               JS::StackCapture&& capture /* = JS::StackCapture(JS::AllFrames()) */)
    1162             : {
    1163        2961 :     MOZ_ASSERT(initialized());
    1164        2961 :     MOZ_RELEASE_ASSERT(cx->compartment());
    1165        2961 :     assertSameCompartment(cx, this);
    1166             : 
    1167       11844 :     if (creatingSavedFrame ||
    1168        8883 :         cx->isExceptionPending() ||
    1169       23688 :         !cx->global() ||
    1170        8883 :         !cx->global()->isStandardClassResolved(JSProto_Object))
    1171             :     {
    1172           0 :         frame.set(nullptr);
    1173           0 :         return true;
    1174             :     }
    1175             : 
    1176        5922 :     AutoGeckoProfilerEntry psuedoFrame(cx->runtime(), "js::SavedStacks::saveCurrentStack");
    1177        5922 :     FrameIter iter(cx);
    1178        2961 :     return insertFrames(cx, iter, frame, mozilla::Move(capture));
    1179             : }
    1180             : 
    1181             : bool
    1182           0 : SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString asyncCause,
    1183             :                             MutableHandleSavedFrame adoptedStack, uint32_t maxFrameCount)
    1184             : {
    1185           0 :     MOZ_ASSERT(initialized());
    1186           0 :     MOZ_RELEASE_ASSERT(cx->compartment());
    1187           0 :     assertSameCompartment(cx, this);
    1188             : 
    1189           0 :     RootedObject asyncStackObj(cx, CheckedUnwrap(asyncStack));
    1190           0 :     MOZ_RELEASE_ASSERT(asyncStackObj);
    1191           0 :     MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*asyncStackObj));
    1192           0 :     RootedSavedFrame frame(cx, &asyncStackObj->as<js::SavedFrame>());
    1193             : 
    1194           0 :     return adoptAsyncStack(cx, frame, asyncCause, adoptedStack, maxFrameCount);
    1195             : }
    1196             : 
    1197             : void
    1198           0 : SavedStacks::sweep()
    1199             : {
    1200           0 :     frames.sweep();
    1201           0 :     pcLocationMap.sweep();
    1202           0 : }
    1203             : 
    1204             : void
    1205          26 : SavedStacks::trace(JSTracer* trc)
    1206             : {
    1207          26 :     pcLocationMap.trace(trc);
    1208          26 : }
    1209             : 
    1210             : uint32_t
    1211           0 : SavedStacks::count()
    1212             : {
    1213           0 :     MOZ_ASSERT(initialized());
    1214           0 :     return frames.count();
    1215             : }
    1216             : 
    1217             : void
    1218          15 : SavedStacks::clear()
    1219             : {
    1220          15 :     frames.clear();
    1221          15 : }
    1222             : 
    1223             : size_t
    1224           0 : SavedStacks::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
    1225             : {
    1226           0 :     return frames.sizeOfExcludingThis(mallocSizeOf) +
    1227           0 :            pcLocationMap.sizeOfExcludingThis(mallocSizeOf);
    1228             : }
    1229             : 
    1230             : // Given that we have captured a stqck frame with the given principals and
    1231             : // source, return true if the requested `StackCapture` has been satisfied and
    1232             : // stack walking can halt. Return false otherwise (and stack walking and frame
    1233             : // capturing should continue).
    1234             : static inline bool
    1235       12463 : captureIsSatisfied(JSContext* cx, JSPrincipals* principals, const JSAtom* source,
    1236             :                    JS::StackCapture& capture)
    1237             : {
    1238             :     class Matcher
    1239             :     {
    1240             :         JSContext* cx_;
    1241             :         JSPrincipals* framePrincipals_;
    1242             :         const JSAtom* frameSource_;
    1243             : 
    1244             :       public:
    1245       12463 :         Matcher(JSContext* cx, JSPrincipals* principals, const JSAtom* source)
    1246       12463 :           : cx_(cx)
    1247             :           , framePrincipals_(principals)
    1248       12463 :           , frameSource_(source)
    1249       12463 :         { }
    1250             : 
    1251           0 :         bool match(JS::FirstSubsumedFrame& target) {
    1252           0 :             auto subsumes = cx_->runtime()->securityCallbacks->subsumes;
    1253           0 :             return (!subsumes || subsumes(target.principals, framePrincipals_)) &&
    1254           0 :                    (!target.ignoreSelfHosted || frameSource_ != cx_->names().selfHosted);
    1255             :         }
    1256             : 
    1257        9822 :         bool match(JS::MaxFrames& target) {
    1258        9822 :             return target.maxFrames == 1;
    1259             :         }
    1260             : 
    1261        2641 :         bool match(JS::AllFrames&) {
    1262        2641 :             return false;
    1263             :         }
    1264             :     };
    1265             : 
    1266       12463 :     Matcher m(cx, principals, source);
    1267       12463 :     return capture.match(m);
    1268             : }
    1269             : 
    1270             : bool
    1271        2961 : SavedStacks::insertFrames(JSContext* cx, FrameIter& iter, MutableHandleSavedFrame frame,
    1272             :                           JS::StackCapture&& capture)
    1273             : {
    1274             :     // In order to lookup a cached SavedFrame object, we need to have its parent
    1275             :     // SavedFrame, which means we need to walk the stack from oldest frame to
    1276             :     // youngest. However, FrameIter walks the stack from youngest frame to
    1277             :     // oldest. The solution is to append stack frames to a vector as we walk the
    1278             :     // stack with FrameIter, and then do a second pass through that vector in
    1279             :     // reverse order after the traversal has completed and get or create the
    1280             :     // SavedFrame objects at that time.
    1281             :     //
    1282             :     // To avoid making many copies of FrameIter (whose copy constructor is
    1283             :     // relatively slow), we use a vector of `SavedFrame::Lookup` objects, which
    1284             :     // only contain the FrameIter data we need. The `SavedFrame::Lookup`
    1285             :     // objects are partially initialized with everything except their parent
    1286             :     // pointers on the first pass, and then we fill in the parent pointers as we
    1287             :     // return in the second pass.
    1288             : 
    1289        2961 :     Activation* asyncActivation = nullptr;
    1290        5922 :     RootedSavedFrame asyncStack(cx, nullptr);
    1291        5922 :     RootedString asyncCause(cx, nullptr);
    1292        2961 :     bool parentIsInCache = false;
    1293        5922 :     RootedSavedFrame cachedFrame(cx, nullptr);
    1294             : 
    1295             :     // Accumulate the vector of Lookup objects in |stackChain|.
    1296        5922 :     SavedFrame::AutoLookupVector stackChain(cx);
    1297       27409 :     while (!iter.done()) {
    1298       12521 :         Activation& activation = *iter.activation();
    1299             : 
    1300       12521 :         if (asyncActivation && asyncActivation != &activation) {
    1301             :             // We found an async stack in the previous activation, and we
    1302             :             // walked past the oldest frame of that activation, we're done.
    1303             :             // However, we only want to use the async parent if it was
    1304             :             // explicitly requested; if we got here otherwise, we have
    1305             :             // a direct parent, which we prefer.
    1306          59 :             if (asyncActivation->asyncCallIsExplicit())
    1307         355 :                 break;
    1308           1 :             asyncActivation = nullptr;
    1309             :         }
    1310             : 
    1311       12463 :         if (!asyncActivation) {
    1312       12356 :             asyncStack = activation.asyncStack();
    1313       12356 :             if (asyncStack) {
    1314             :                 // While walking from the youngest to the oldest frame, we found
    1315             :                 // an activation that has an async stack set. We will use the
    1316             :                 // youngest frame of the async stack as the parent of the oldest
    1317             :                 // frame of this activation. We still need to iterate over other
    1318             :                 // frames in this activation before reaching the oldest frame.
    1319         634 :                 AutoCompartmentUnchecked ac(cx, iter.compartment());
    1320         317 :                 const char* cause = activation.asyncCause();
    1321         317 :                 UTF8Chars utf8Chars(cause, strlen(cause));
    1322         317 :                 size_t twoByteCharsLen = 0;
    1323         634 :                 char16_t* twoByteChars = UTF8CharsToNewTwoByteCharsZ(cx, utf8Chars,
    1324         634 :                                                                      &twoByteCharsLen).get();
    1325         317 :                 if (!twoByteChars)
    1326           0 :                     return false;
    1327             : 
    1328             :                 // We expect that there will be a relatively small set of
    1329             :                 // asyncCause reasons ("setTimeout", "promise", etc.), so we
    1330             :                 // atomize the cause here in hopes of being able to benefit
    1331             :                 // from reuse.
    1332         317 :                 asyncCause = JS_AtomizeUCStringN(cx, twoByteChars, twoByteCharsLen);
    1333         317 :                 js_free(twoByteChars);
    1334         317 :                 if (!asyncCause)
    1335           0 :                     return false;
    1336         317 :                 asyncActivation = &activation;
    1337             :             }
    1338             :         }
    1339             : 
    1340       24687 :         Rooted<LocationValue> location(cx);
    1341             :         {
    1342       24926 :             AutoCompartmentUnchecked ac(cx, iter.compartment());
    1343       12463 :             if (!cx->compartment()->savedStacks().getLocation(cx, iter, &location))
    1344           0 :                 return false;
    1345             :         }
    1346             : 
    1347             :         // The bit set means that the next older parent (frame, pc) pair *must*
    1348             :         // be in the cache.
    1349       12463 :         if (capture.is<JS::AllFrames>())
    1350        2641 :             parentIsInCache = iter.hasCachedSavedFrame();
    1351             : 
    1352       12463 :         auto principals = iter.compartment()->principals();
    1353       12463 :         auto displayAtom = (iter.isWasm() || iter.isFunctionFrame()) ? iter.functionDisplayAtom() : nullptr;
    1354       49852 :         if (!stackChain->emplaceBack(location.source(),
    1355       24926 :                                      location.line(),
    1356       24926 :                                      location.column(),
    1357             :                                      displayAtom,
    1358             :                                      nullptr,
    1359             :                                      nullptr,
    1360             :                                      principals,
    1361       24926 :                                      LiveSavedFrameCache::getFramePtr(iter),
    1362       24926 :                                      iter.pc(),
    1363       24926 :                                      &activation))
    1364             :         {
    1365           0 :             ReportOutOfMemory(cx);
    1366           0 :             return false;
    1367             :         }
    1368             : 
    1369       12463 :         if (captureIsSatisfied(cx, principals, location.source(), capture)) {
    1370             :             // The frame we just saved was the last one we were asked to save.
    1371             :             // If we had an async stack, ensure we don't use any of its frames.
    1372           0 :             asyncStack.set(nullptr);
    1373           0 :             break;
    1374             :         }
    1375             : 
    1376       12463 :         ++iter;
    1377             : 
    1378       14226 :         if (parentIsInCache &&
    1379       13980 :             !iter.done() &&
    1380        1517 :             iter.hasCachedSavedFrame())
    1381             :         {
    1382        1517 :             auto* cache = activation.getLiveSavedFrameCache(cx);
    1383        1517 :             if (!cache)
    1384           0 :                 return false;
    1385        1517 :             cache->find(cx, iter, &cachedFrame);
    1386        1517 :             if (cachedFrame)
    1387         239 :                 break;
    1388             :         }
    1389             : 
    1390       12224 :         if (capture.is<JS::MaxFrames>())
    1391        9822 :             capture.as<JS::MaxFrames>().maxFrames--;
    1392             :     }
    1393             : 
    1394             :     // Limit the depth of the async stack, if any, and ensure that the
    1395             :     // SavedFrame instances we use are stored in the same compartment as the
    1396             :     // rest of the synchronous stack chain.
    1397        5922 :     RootedSavedFrame parentFrame(cx, cachedFrame);
    1398        2961 :     if (asyncStack && !capture.is<JS::FirstSubsumedFrame>()) {
    1399         316 :         uint32_t maxAsyncFrames = capture.is<JS::MaxFrames>()
    1400         316 :             ? capture.as<JS::MaxFrames>().maxFrames
    1401         316 :             : ASYNC_STACK_MAX_FRAME_COUNT;
    1402         316 :         if (!adoptAsyncStack(cx, asyncStack, asyncCause, &parentFrame, maxAsyncFrames))
    1403           0 :             return false;
    1404             :     }
    1405             : 
    1406             :     // Iterate through |stackChain| in reverse order and get or create the
    1407             :     // actual SavedFrame instances.
    1408       15424 :     for (size_t i = stackChain->length(); i != 0; i--) {
    1409       12463 :         SavedFrame::HandleLookup lookup = stackChain[i-1];
    1410       12463 :         lookup->parent = parentFrame;
    1411       12463 :         parentFrame.set(getOrCreateSavedFrame(cx, lookup));
    1412       12463 :         if (!parentFrame)
    1413           0 :             return false;
    1414             : 
    1415       12463 :         if (capture.is<JS::AllFrames>() && lookup->framePtr && parentFrame != cachedFrame) {
    1416        2641 :             auto* cache = lookup->activation->getLiveSavedFrameCache(cx);
    1417        2641 :             if (!cache || !cache->insert(cx, *lookup->framePtr, lookup->pc, parentFrame))
    1418           0 :                 return false;
    1419             :         }
    1420             :     }
    1421             : 
    1422        2961 :     frame.set(parentFrame);
    1423        2961 :     return true;
    1424             : }
    1425             : 
    1426             : bool
    1427         316 : SavedStacks::adoptAsyncStack(JSContext* cx, HandleSavedFrame asyncStack,
    1428             :                              HandleString asyncCause,
    1429             :                              MutableHandleSavedFrame adoptedStack,
    1430             :                              uint32_t maxFrameCount)
    1431             : {
    1432         632 :     RootedAtom asyncCauseAtom(cx, AtomizeString(cx, asyncCause));
    1433         316 :     if (!asyncCauseAtom)
    1434           0 :         return false;
    1435             : 
    1436             :     // If maxFrameCount is zero, the caller asked for an unlimited number of
    1437             :     // stack frames, but async stacks are not limited by the available stack
    1438             :     // memory, so we need to set an arbitrary limit when collecting them. We
    1439             :     // still don't enforce an upper limit if the caller requested more frames.
    1440         316 :     uint32_t maxFrames = maxFrameCount > 0 ? maxFrameCount : ASYNC_STACK_MAX_FRAME_COUNT;
    1441             : 
    1442             :     // Accumulate the vector of Lookup objects in |stackChain|.
    1443         632 :     SavedFrame::AutoLookupVector stackChain(cx);
    1444         316 :     SavedFrame* currentSavedFrame = asyncStack;
    1445         316 :     SavedFrame* firstSavedFrameParent = nullptr;
    1446        2516 :     for (uint32_t i = 0; i < maxFrames && currentSavedFrame; i++) {
    1447        2200 :         if (!stackChain->emplaceBack(*currentSavedFrame)) {
    1448           0 :             ReportOutOfMemory(cx);
    1449           0 :             return false;
    1450             :         }
    1451             : 
    1452        2200 :         currentSavedFrame = currentSavedFrame->getParent();
    1453             : 
    1454             :         // Attach the asyncCause to the youngest frame.
    1455        2200 :         if (i == 0) {
    1456         316 :             stackChain->back().asyncCause = asyncCauseAtom;
    1457         316 :             firstSavedFrameParent = currentSavedFrame;
    1458             :         }
    1459             :     }
    1460             : 
    1461             :     // This is the 1-based index of the oldest frame we care about.
    1462         316 :     size_t oldestFramePosition = stackChain->length();
    1463         632 :     RootedSavedFrame parentFrame(cx, nullptr);
    1464             : 
    1465         632 :     if (currentSavedFrame == nullptr &&
    1466         316 :         asyncStack->compartment() == cx->compartment()) {
    1467             :         // If we consumed the full async stack, and the stack is in the same
    1468             :         // compartment as the one requested, we don't need to rebuild the full
    1469             :         // chain again using the lookup objects, we can just reference the
    1470             :         // existing chain and change the asyncCause on the younger frame.
    1471         139 :         oldestFramePosition = 1;
    1472         139 :         parentFrame = firstSavedFrameParent;
    1473         177 :     } else if (maxFrameCount == 0 &&
    1474             :                oldestFramePosition == ASYNC_STACK_MAX_FRAME_COUNT) {
    1475             :         // If we captured the maximum number of frames and the caller requested
    1476             :         // no specific limit, we only return half of them. This means that for
    1477             :         // the next iterations, it's likely we can use the optimization above.
    1478           0 :         oldestFramePosition = ASYNC_STACK_MAX_FRAME_COUNT / 2;
    1479             :     }
    1480             : 
    1481             :     // Iterate through |stackChain| in reverse order and get or create the
    1482             :     // actual SavedFrame instances.
    1483        1906 :     for (size_t i = oldestFramePosition; i != 0; i--) {
    1484        1590 :         SavedFrame::HandleLookup lookup = stackChain[i-1];
    1485        1590 :         lookup->parent = parentFrame;
    1486        1590 :         parentFrame.set(getOrCreateSavedFrame(cx, lookup));
    1487        1590 :         if (!parentFrame)
    1488           0 :             return false;
    1489             :     }
    1490             : 
    1491         316 :     adoptedStack.set(parentFrame);
    1492         316 :     return true;
    1493             : }
    1494             : 
    1495             : SavedFrame*
    1496       14053 : SavedStacks::getOrCreateSavedFrame(JSContext* cx, SavedFrame::HandleLookup lookup)
    1497             : {
    1498       14053 :     const SavedFrame::Lookup& lookupInstance = lookup.get();
    1499       14053 :     DependentAddPtr<SavedFrame::Set> p(cx, frames, lookupInstance);
    1500       14053 :     if (p) {
    1501       12037 :         MOZ_ASSERT(*p);
    1502       12037 :         return *p;
    1503             :     }
    1504             : 
    1505        4032 :     RootedSavedFrame frame(cx, createFrameFromLookup(cx, lookup));
    1506        2016 :     if (!frame)
    1507           0 :         return nullptr;
    1508             : 
    1509        2016 :     if (!p.add(cx, frames, lookupInstance, frame))
    1510           0 :         return nullptr;
    1511             : 
    1512        2016 :     return frame;
    1513             : }
    1514             : 
    1515             : SavedFrame*
    1516        2016 : SavedStacks::createFrameFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup)
    1517             : {
    1518        4032 :     RootedSavedFrame frame(cx, SavedFrame::create(cx));
    1519        2016 :     if (!frame)
    1520           0 :         return nullptr;
    1521        2016 :     frame->initFromLookup(cx, lookup);
    1522             : 
    1523        2016 :     if (!FreezeObject(cx, frame))
    1524           0 :         return nullptr;
    1525             : 
    1526        2016 :     return frame;
    1527             : }
    1528             : 
    1529             : bool
    1530       12463 : SavedStacks::getLocation(JSContext* cx, const FrameIter& iter,
    1531             :                          MutableHandle<LocationValue> locationp)
    1532             : {
    1533             :     // We should only ever be caching location values for scripts in this
    1534             :     // compartment. Otherwise, we would get dead cross-compartment scripts in
    1535             :     // the cache because our compartment's sweep method isn't called when their
    1536             :     // compartment gets collected.
    1537       12463 :     assertSameCompartment(cx, this, iter.compartment());
    1538             : 
    1539             :     // When we have a |JSScript| for this frame, use a potentially memoized
    1540             :     // location from our PCLocationMap and copy it into |locationp|. When we do
    1541             :     // not have a |JSScript| for this frame (wasm frames), we take a slow path
    1542             :     // that doesn't employ memoization, and update |locationp|'s slots directly.
    1543             : 
    1544       12463 :     if (!iter.hasScript()) {
    1545           0 :         if (const char16_t* displayURL = iter.displayURL()) {
    1546           0 :             locationp.setSource(AtomizeChars(cx, displayURL, js_strlen(displayURL)));
    1547             :         } else {
    1548           0 :             const char* filename = iter.filename() ? iter.filename() : "";
    1549           0 :             locationp.setSource(Atomize(cx, filename, strlen(filename)));
    1550             :         }
    1551           0 :         if (!locationp.source())
    1552           0 :             return false;
    1553             : 
    1554           0 :         uint32_t column = 0;
    1555           0 :         locationp.setLine(iter.computeLine(&column));
    1556             :         // XXX: Make the column 1-based as in other browsers, instead of 0-based
    1557             :         // which is how SpiderMonkey stores it internally. This will be
    1558             :         // unnecessary once bug 1144340 is fixed.
    1559           0 :         locationp.setColumn(column + 1);
    1560           0 :         return true;
    1561             :     }
    1562             : 
    1563       24926 :     RootedScript script(cx, iter.script());
    1564       12463 :     jsbytecode* pc = iter.pc();
    1565             : 
    1566       24926 :     PCKey key(script, pc);
    1567       12463 :     PCLocationMap::AddPtr p = pcLocationMap.lookupForAdd(key);
    1568             : 
    1569       12463 :     if (!p) {
    1570        1444 :         RootedAtom source(cx);
    1571         722 :         if (const char16_t* displayURL = iter.displayURL()) {
    1572           0 :             source = AtomizeChars(cx, displayURL, js_strlen(displayURL));
    1573             :         } else {
    1574         722 :             const char* filename = script->filename() ? script->filename() : "";
    1575         722 :             source = Atomize(cx, filename, strlen(filename));
    1576             :         }
    1577         722 :         if (!source)
    1578           0 :             return false;
    1579             : 
    1580             :         uint32_t column;
    1581         722 :         uint32_t line = PCToLineNumber(script, pc, &column);
    1582             : 
    1583             :         // Make the column 1-based. See comment above.
    1584        1444 :         LocationValue value(source, line, column + 1);
    1585         722 :         if (!pcLocationMap.add(p, key, value)) {
    1586           0 :             ReportOutOfMemory(cx);
    1587           0 :             return false;
    1588             :         }
    1589             :     }
    1590             : 
    1591       12463 :     locationp.set(p->value());
    1592       12463 :     return true;
    1593             : }
    1594             : 
    1595             : void
    1596           0 : SavedStacks::chooseSamplingProbability(JSCompartment* compartment)
    1597             : {
    1598           0 :     GlobalObject* global = compartment->maybeGlobal();
    1599           0 :     if (!global)
    1600           0 :         return;
    1601             : 
    1602           0 :     GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
    1603           0 :     if (!dbgs || dbgs->empty())
    1604           0 :         return;
    1605             : 
    1606           0 :     mozilla::DebugOnly<ReadBarriered<Debugger*>*> begin = dbgs->begin();
    1607           0 :     mozilla::DebugOnly<bool> foundAnyDebuggers = false;
    1608             : 
    1609           0 :     double probability = 0;
    1610           0 :     for (auto dbgp = dbgs->begin(); dbgp < dbgs->end(); dbgp++) {
    1611             :         // The set of debuggers had better not change while we're iterating,
    1612             :         // such that the vector gets reallocated.
    1613           0 :         MOZ_ASSERT(dbgs->begin() == begin);
    1614             : 
    1615           0 :         if ((*dbgp)->trackingAllocationSites && (*dbgp)->enabled) {
    1616           0 :             foundAnyDebuggers = true;
    1617           0 :             probability = std::max((*dbgp)->allocationSamplingProbability,
    1618           0 :                                    probability);
    1619             :         }
    1620             :     }
    1621           0 :     MOZ_ASSERT(foundAnyDebuggers);
    1622             : 
    1623           0 :     if (!bernoulliSeeded) {
    1624           0 :         mozilla::Array<uint64_t, 2> seed;
    1625           0 :         GenerateXorShift128PlusSeed(seed);
    1626           0 :         bernoulli.setRandomState(seed[0], seed[1]);
    1627           0 :         bernoulliSeeded = true;
    1628             :     }
    1629             : 
    1630           0 :     bernoulli.setProbability(probability);
    1631             : }
    1632             : 
    1633             : JSObject*
    1634           0 : SavedStacks::MetadataBuilder::build(JSContext* cx, HandleObject target,
    1635             :                                     AutoEnterOOMUnsafeRegion& oomUnsafe) const
    1636             : {
    1637           0 :     RootedObject obj(cx, target);
    1638             : 
    1639           0 :     SavedStacks& stacks = cx->compartment()->savedStacks();
    1640           0 :     if (!stacks.bernoulli.trial())
    1641           0 :         return nullptr;
    1642             : 
    1643           0 :     RootedSavedFrame frame(cx);
    1644           0 :     if (!stacks.saveCurrentStack(cx, &frame))
    1645           0 :         oomUnsafe.crash("SavedStacksMetadataBuilder");
    1646             : 
    1647           0 :     if (!Debugger::onLogAllocationSite(cx, obj, frame, mozilla::TimeStamp::Now()))
    1648           0 :         oomUnsafe.crash("SavedStacksMetadataBuilder");
    1649             : 
    1650           0 :     MOZ_ASSERT_IF(frame, !frame->is<WrapperObject>());
    1651           0 :     return frame;
    1652             : }
    1653             : 
    1654           3 : const SavedStacks::MetadataBuilder SavedStacks::metadataBuilder;
    1655             : 
    1656             : #ifdef JS_CRASH_DIAGNOSTICS
    1657             : void
    1658       15424 : CompartmentChecker::check(SavedStacks* stacks)
    1659             : {
    1660       15424 :     if (&compartment->savedStacks() != stacks) {
    1661             :         printf("*** Compartment SavedStacks mismatch: %p vs. %p\n",
    1662           0 :                (void*) &compartment->savedStacks(), stacks);
    1663           0 :         MOZ_CRASH();
    1664             :     }
    1665       15424 : }
    1666             : #endif /* JS_CRASH_DIAGNOSTICS */
    1667             : 
    1668           3 : /* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsSystem;
    1669           3 : /* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsNotSystem;
    1670             : 
    1671             : UTF8CharsZ
    1672           0 : BuildUTF8StackString(JSContext* cx, HandleObject stack)
    1673             : {
    1674           0 :     RootedString stackStr(cx);
    1675           0 :     if (!JS::BuildStackString(cx, stack, &stackStr))
    1676           0 :         return UTF8CharsZ();
    1677             : 
    1678           0 :     char* chars = JS_EncodeStringToUTF8(cx, stackStr);
    1679           0 :     return UTF8CharsZ(chars, strlen(chars));
    1680             : }
    1681             : 
    1682             : } /* namespace js */
    1683             : 
    1684             : namespace JS {
    1685             : namespace ubi {
    1686             : 
    1687             : bool
    1688           0 : ConcreteStackFrame<SavedFrame>::isSystem() const
    1689             : {
    1690           0 :     auto trustedPrincipals = get().runtimeFromAnyThread()->trustedPrincipals();
    1691           0 :     return get().getPrincipals() == trustedPrincipals ||
    1692           0 :            get().getPrincipals() == &js::ReconstructedSavedFramePrincipals::IsSystem;
    1693             : }
    1694             : 
    1695             : bool
    1696           0 : ConcreteStackFrame<SavedFrame>::constructSavedFrameStack(JSContext* cx,
    1697             :                                                          MutableHandleObject outSavedFrameStack)
    1698             :     const
    1699             : {
    1700           0 :     outSavedFrameStack.set(&get());
    1701           0 :     if (!cx->compartment()->wrap(cx, outSavedFrameStack)) {
    1702           0 :         outSavedFrameStack.set(nullptr);
    1703           0 :         return false;
    1704             :     }
    1705           0 :     return true;
    1706             : }
    1707             : 
    1708             : // A `mozilla::Variant` matcher that converts the inner value of a
    1709             : // `JS::ubi::AtomOrTwoByteChars` string to a `JSAtom*`.
    1710             : struct MOZ_STACK_CLASS AtomizingMatcher
    1711             : {
    1712             :     JSContext* cx;
    1713             :     size_t     length;
    1714             : 
    1715           0 :     explicit AtomizingMatcher(JSContext* cx, size_t length)
    1716           0 :       : cx(cx)
    1717           0 :       , length(length)
    1718           0 :     { }
    1719             : 
    1720           0 :     JSAtom* match(JSAtom* atom) {
    1721           0 :         MOZ_ASSERT(atom);
    1722           0 :         return atom;
    1723             :     }
    1724             : 
    1725           0 :     JSAtom* match(const char16_t* chars) {
    1726           0 :         MOZ_ASSERT(chars);
    1727           0 :         return AtomizeChars(cx, chars, length);
    1728             :     }
    1729             : };
    1730             : 
    1731             : JS_PUBLIC_API(bool)
    1732           0 : ConstructSavedFrameStackSlow(JSContext* cx, JS::ubi::StackFrame& frame,
    1733             :                              MutableHandleObject outSavedFrameStack)
    1734             : {
    1735           0 :     SavedFrame::AutoLookupVector stackChain(cx);
    1736           0 :     Rooted<JS::ubi::StackFrame> ubiFrame(cx, frame);
    1737             : 
    1738           0 :     while (ubiFrame.get()) {
    1739             :         // Convert the source and functionDisplayName strings to atoms.
    1740             : 
    1741           0 :         js::RootedAtom source(cx);
    1742           0 :         AtomizingMatcher atomizer(cx, ubiFrame.get().sourceLength());
    1743           0 :         source = ubiFrame.get().source().match(atomizer);
    1744           0 :         if (!source)
    1745           0 :             return false;
    1746             : 
    1747           0 :         js::RootedAtom functionDisplayName(cx);
    1748           0 :         auto nameLength = ubiFrame.get().functionDisplayNameLength();
    1749           0 :         if (nameLength > 0) {
    1750           0 :             AtomizingMatcher atomizer(cx, nameLength);
    1751           0 :             functionDisplayName = ubiFrame.get().functionDisplayName().match(atomizer);
    1752           0 :             if (!functionDisplayName)
    1753           0 :                 return false;
    1754             :         }
    1755             : 
    1756           0 :         auto principals = js::ReconstructedSavedFramePrincipals::getSingleton(ubiFrame.get());
    1757             : 
    1758           0 :         if (!stackChain->emplaceBack(source, ubiFrame.get().line(), ubiFrame.get().column(),
    1759             :                                      functionDisplayName, /* asyncCause */ nullptr,
    1760             :                                      /* parent */ nullptr, principals))
    1761             :         {
    1762           0 :             ReportOutOfMemory(cx);
    1763           0 :             return false;
    1764             :         }
    1765             : 
    1766           0 :         ubiFrame = ubiFrame.get().parent();
    1767             :     }
    1768             : 
    1769           0 :     js::RootedSavedFrame parentFrame(cx);
    1770           0 :     for (size_t i = stackChain->length(); i != 0; i--) {
    1771           0 :         SavedFrame::HandleLookup lookup = stackChain[i-1];
    1772           0 :         lookup->parent = parentFrame;
    1773           0 :         parentFrame = cx->compartment()->savedStacks().getOrCreateSavedFrame(cx, lookup);
    1774           0 :         if (!parentFrame)
    1775           0 :             return false;
    1776             :     }
    1777             : 
    1778           0 :     outSavedFrameStack.set(parentFrame);
    1779           0 :     return true;
    1780             : }
    1781             : 
    1782             : 
    1783             : } // namespace ubi
    1784             : } // namespace JS

Generated by: LCOV version 1.13