diff options
Diffstat (limited to 'js/src/vm/SavedFrame.h')
-rw-r--r-- | js/src/vm/SavedFrame.h | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/js/src/vm/SavedFrame.h b/js/src/vm/SavedFrame.h new file mode 100644 index 000000000..ee4cfe03d --- /dev/null +++ b/js/src/vm/SavedFrame.h @@ -0,0 +1,323 @@ +/* -*- 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 "mozilla/Attributes.h" + +#include "jswrapper.h" + +#include "js/GCHashTable.h" +#include "js/UbiNode.h" + +namespace js { + +class SavedFrame : public NativeObject { + friend class SavedStacks; + friend struct ::JSStructuredCloneReader; + + static const ClassSpec classSpec_; + + 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(JSContext* cx); + + // 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 JS::GCHashSet<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 MOZ_MUST_USE 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 + }; +}; + +struct SavedFrame::HashPolicy +{ + typedef SavedFrame::Lookup Lookup; + typedef MovableCellHasher<SavedFrame*> SavedFramePtrHasher; + typedef PointerHasher<JSPrincipals*, 3> JSPrincipalsPtrHasher; + + static bool hasHash(const Lookup& l); + static bool ensureHash(const Lookup& l); + 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); +}; + +template <> +struct FallibleHashMethods<SavedFrame::HashPolicy> +{ + template <typename Lookup> static bool hasHash(Lookup&& l) { + return SavedFrame::HashPolicy::hasHash(mozilla::Forward<Lookup>(l)); + } + template <typename Lookup> static bool ensureHash(Lookup&& l) { + return SavedFrame::HashPolicy::ensureHash(mozilla::Forward<Lookup>(l)); + } +}; + +// 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; + } + + MOZ_MUST_USE 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(JSContext* cx) const override { + return get().isSelfHosted(cx); + } + + bool isSystem() const override; + + MOZ_MUST_USE bool constructSavedFrameStack(JSContext* cx, + MutableHandleObject outSavedFrameStack) + const override; +}; + +} // namespace ubi +} // namespace JS + +#endif // vm_SavedFrame_h |