| /* -*- 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 |