blob: 484ba0feac34179ba97a420adfc0a1166efa4177 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_SavedFrame_h
#define vm_SavedFrame_h
#include "jswrapper.h"
#include "js/GCHashTable.h"
#include "js/UbiNode.h"
namespace js {
class SavedFrame : public NativeObject {
friend class SavedStacks;
friend struct ::JSStructuredCloneReader;
public:
static const Class class_;
static const JSPropertySpec protoAccessors[];
static const JSFunctionSpec protoFunctions[];
static const JSFunctionSpec staticFunctions[];
// Prototype methods and properties to be exposed to JS.
static bool construct(JSContext* cx, unsigned argc, Value* vp);
static bool sourceProperty(JSContext* cx, unsigned argc, Value* vp);
static bool lineProperty(JSContext* cx, unsigned argc, Value* vp);
static bool columnProperty(JSContext* cx, unsigned argc, Value* vp);
static bool functionDisplayNameProperty(JSContext* cx, unsigned argc, Value* vp);
static bool asyncCauseProperty(JSContext* cx, unsigned argc, Value* vp);
static bool asyncParentProperty(JSContext* cx, unsigned argc, Value* vp);
static bool parentProperty(JSContext* cx, unsigned argc, Value* vp);
static bool toStringMethod(JSContext* cx, unsigned argc, Value* vp);
static void finalize(FreeOp* fop, JSObject* obj);
// Convenient getters for SavedFrame's reserved slots for use from C++.
JSAtom* getSource();
uint32_t getLine();
uint32_t getColumn();
JSAtom* getFunctionDisplayName();
JSAtom* getAsyncCause();
SavedFrame* getParent() const;
JSPrincipals* getPrincipals();
bool isSelfHosted();
// Iterators for use with C++11 range based for loops, eg:
//
// SavedFrame* stack = getSomeSavedFrameStack();
// for (const SavedFrame* frame : *stack) {
// ...
// }
//
// If you need to keep each frame rooted during iteration, you can use
// `SavedFrame::RootedRange`. Each frame yielded by
// `SavedFrame::RootedRange` is only a valid handle to a rooted `SavedFrame`
// within the loop's block for a single loop iteration. When the next
// iteration begins, the value is invalidated.
//
// RootedSavedFrame stack(cx, getSomeSavedFrameStack());
// for (HandleSavedFrame frame : SavedFrame::RootedRange(cx, stack)) {
// ...
// }
class Iterator {
SavedFrame* frame_;
public:
explicit Iterator(SavedFrame* frame) : frame_(frame) { }
SavedFrame& operator*() const { MOZ_ASSERT(frame_); return *frame_; }
bool operator!=(const Iterator& rhs) const { return rhs.frame_ != frame_; }
inline void operator++();
};
Iterator begin() { return Iterator(this); }
Iterator end() { return Iterator(nullptr); }
class ConstIterator {
const SavedFrame* frame_;
public:
explicit ConstIterator(const SavedFrame* frame) : frame_(frame) { }
const SavedFrame& operator*() const { MOZ_ASSERT(frame_); return *frame_; }
bool operator!=(const ConstIterator& rhs) const { return rhs.frame_ != frame_; }
inline void operator++();
};
ConstIterator begin() const { return ConstIterator(this); }
ConstIterator end() const { return ConstIterator(nullptr); }
class RootedRange;
class MOZ_STACK_CLASS RootedIterator {
friend class RootedRange;
RootedRange* range_;
// For use by RootedRange::end() only.
explicit RootedIterator() : range_(nullptr) { }
public:
explicit RootedIterator(RootedRange& range) : range_(&range) { }
HandleSavedFrame operator*() { MOZ_ASSERT(range_); return range_->frame_; }
bool operator!=(const RootedIterator& rhs) const {
// We should only ever compare to the null range, aka we are just
// testing if this range is done.
MOZ_ASSERT(rhs.range_ == nullptr);
return range_->frame_ != nullptr;
}
inline void operator++();
};
class MOZ_STACK_CLASS RootedRange {
friend class RootedIterator;
RootedSavedFrame frame_;
public:
RootedRange(JSContext* cx, HandleSavedFrame frame) : frame_(cx, frame) { }
RootedIterator begin() { return RootedIterator(*this); }
RootedIterator end() { return RootedIterator(); }
};
static bool isSavedFrameAndNotProto(JSObject& obj) {
return obj.is<SavedFrame>() &&
!obj.as<SavedFrame>().getReservedSlot(JSSLOT_SOURCE).isNull();
}
static bool isSavedFrameOrWrapperAndNotProto(JSObject& obj) {
auto unwrapped = CheckedUnwrap(&obj);
if (!unwrapped)
return false;
return isSavedFrameAndNotProto(*unwrapped);
}
struct Lookup;
struct HashPolicy;
typedef GCHashSet<js::ReadBarriered<SavedFrame*>,
HashPolicy,
SystemAllocPolicy> Set;
class AutoLookupVector;
class MOZ_STACK_CLASS HandleLookup {
friend class AutoLookupVector;
Lookup& lookup;
explicit HandleLookup(Lookup& lookup) : lookup(lookup) { }
public:
inline Lookup& get() { return lookup; }
inline Lookup* operator->() { return &lookup; }
};
private:
static SavedFrame* create(JSContext* cx);
static bool finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject proto);
void initFromLookup(HandleLookup lookup);
void initSource(JSAtom* source);
void initLine(uint32_t line);
void initColumn(uint32_t column);
void initFunctionDisplayName(JSAtom* maybeName);
void initAsyncCause(JSAtom* maybeCause);
void initParent(SavedFrame* maybeParent);
void initPrincipalsAlreadyHeld(JSPrincipals* principals);
void initPrincipals(JSPrincipals* principals);
enum {
// The reserved slots in the SavedFrame class.
JSSLOT_SOURCE,
JSSLOT_LINE,
JSSLOT_COLUMN,
JSSLOT_FUNCTIONDISPLAYNAME,
JSSLOT_ASYNCCAUSE,
JSSLOT_PARENT,
JSSLOT_PRINCIPALS,
// The total number of reserved slots in the SavedFrame class.
JSSLOT_COUNT
};
static bool checkThis(JSContext* cx, CallArgs& args, const char* fnName,
MutableHandleObject frame);
};
struct SavedFrame::HashPolicy
{
typedef SavedFrame::Lookup Lookup;
typedef MovableCellHasher<SavedFrame*> SavedFramePtrHasher;
typedef PointerHasher<JSPrincipals*, 3> JSPrincipalsPtrHasher;
static HashNumber hash(const Lookup& lookup);
static bool match(SavedFrame* existing, const Lookup& lookup);
typedef ReadBarriered<SavedFrame*> Key;
static void rekey(Key& key, const Key& newKey);
};
// Assert that if the given object is not null, that it must be either a
// SavedFrame object or wrapper (Xray or CCW) around a SavedFrame object.
inline void AssertObjectIsSavedFrameOrWrapper(JSContext* cx, HandleObject stack);
// When we reconstruct a SavedFrame stack from a JS::ubi::StackFrame, we may not
// have access to the principals that the original stack was captured
// with. Instead, we use these two singleton principals based on whether
// JS::ubi::StackFrame::isSystem or not. These singletons should never be passed
// to the subsumes callback, and should be special cased with a shortcut before
// that.
struct ReconstructedSavedFramePrincipals : public JSPrincipals
{
explicit ReconstructedSavedFramePrincipals()
: JSPrincipals()
{
MOZ_ASSERT(is(this));
this->refcount = 1;
}
bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
MOZ_ASSERT(false, "ReconstructedSavedFramePrincipals should never be exposed to embedders");
return false;
}
static ReconstructedSavedFramePrincipals IsSystem;
static ReconstructedSavedFramePrincipals IsNotSystem;
// Return true if the given JSPrincipals* points to one of the
// ReconstructedSavedFramePrincipals singletons, false otherwise.
static bool is(JSPrincipals* p) { return p == &IsSystem || p == &IsNotSystem;}
// Get the appropriate ReconstructedSavedFramePrincipals singleton for the
// given JS::ubi::StackFrame that is being reconstructed as a SavedFrame
// stack.
static JSPrincipals* getSingleton(JS::ubi::StackFrame& f) {
return f.isSystem() ? &IsSystem : &IsNotSystem;
}
};
inline void
SavedFrame::Iterator::operator++()
{
frame_ = frame_->getParent();
}
inline void
SavedFrame::ConstIterator::operator++()
{
frame_ = frame_->getParent();
}
inline void
SavedFrame::RootedIterator::operator++()
{
MOZ_ASSERT(range_);
range_->frame_ = range_->frame_->getParent();
}
} // namespace js
namespace JS {
namespace ubi {
using js::SavedFrame;
// A concrete JS::ubi::StackFrame that is backed by a live SavedFrame object.
template<>
class ConcreteStackFrame<SavedFrame> : public BaseStackFrame {
explicit ConcreteStackFrame(SavedFrame* ptr) : BaseStackFrame(ptr) { }
SavedFrame& get() const { return *static_cast<SavedFrame*>(ptr); }
public:
static void construct(void* storage, SavedFrame* ptr) { new (storage) ConcreteStackFrame(ptr); }
StackFrame parent() const override { return get().getParent(); }
uint32_t line() const override { return get().getLine(); }
uint32_t column() const override { return get().getColumn(); }
AtomOrTwoByteChars source() const override {
auto source = get().getSource();
return AtomOrTwoByteChars(source);
}
AtomOrTwoByteChars functionDisplayName() const override {
auto name = get().getFunctionDisplayName();
return AtomOrTwoByteChars(name);
}
void trace(JSTracer* trc) override {
JSObject* prev = &get();
JSObject* next = prev;
js::TraceRoot(trc, &next, "ConcreteStackFrame<SavedFrame>::ptr");
if (next != prev)
ptr = next;
}
bool isSelfHosted() const override { return get().isSelfHosted(); }
bool isSystem() const override;
bool constructSavedFrameStack(JSContext* cx,
MutableHandleObject outSavedFrameStack) const override;
};
} // namespace ubi
} // namespace JS
#endif // vm_SavedFrame_h