summaryrefslogtreecommitdiffstats
path: root/js/src/vm/SavedFrame.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/vm/SavedFrame.h297
1 files changed, 297 insertions, 0 deletions
diff --git a/js/src/vm/SavedFrame.h b/js/src/vm/SavedFrame.h
new file mode 100644
index 0000000000..a67594fec9
--- /dev/null
+++ b/js/src/vm/SavedFrame.h
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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 "gc/Policy.h"
+#include "js/GCHashTable.h"
+#include "js/Principals.h"
+#include "js/UbiNode.h"
+#include "vm/NativeObject.h"
+
+namespace js {
+
+class SavedFrame : public NativeObject {
+ friend class SavedStacks;
+ friend struct ::JSStructuredCloneReader;
+
+ static const ClassSpec classSpec_;
+
+ public:
+ static const JSClass class_;
+ static const JSClass protoClass_;
+ 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 sourceIdProperty(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(JS::GCContext* gcx, JSObject* obj);
+
+ // Convenient getters for SavedFrame's reserved slots for use from C++.
+ JSAtom* getSource();
+ uint32_t getSourceId();
+ uint32_t getLine();
+ uint32_t getColumn();
+ JSAtom* getFunctionDisplayName();
+ JSAtom* getAsyncCause();
+ SavedFrame* getParent() const;
+ JSPrincipals* getPrincipals();
+ bool getMutedErrors();
+ bool isSelfHosted(JSContext* cx);
+ bool isWasm();
+
+ // When isWasm():
+ uint32_t wasmFuncIndex();
+ uint32_t wasmBytecodeOffset();
+
+ // Iterator for use with C++11 range based for loops, eg:
+ //
+ // Rooted<SavedFrame*> stack(cx, getSomeSavedFrameStack());
+ // for (Handle<SavedFrame*> frame : SavedFrame::RootedRange(cx, stack)) {
+ // ...
+ // }
+ //
+ // 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.
+
+ 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) {}
+ Handle<SavedFrame*> 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;
+ Rooted<SavedFrame*> frame_;
+
+ public:
+ RootedRange(JSContext* cx, Handle<SavedFrame*> frame) : frame_(cx, frame) {}
+ RootedIterator begin() { return RootedIterator(*this); }
+ RootedIterator end() { return RootedIterator(); }
+ };
+
+ struct Lookup;
+ struct HashPolicy;
+
+ typedef JS::GCHashSet<WeakHeapPtr<SavedFrame*>, HashPolicy, SystemAllocPolicy>
+ Set;
+
+ private:
+ static SavedFrame* create(JSContext* cx);
+ [[nodiscard]] static bool finishSavedFrameInit(JSContext* cx,
+ HandleObject ctor,
+ HandleObject proto);
+ void initFromLookup(JSContext* cx, Handle<Lookup> lookup);
+ void initSource(JSAtom* source);
+ void initSourceId(uint32_t id);
+ void initLine(uint32_t line);
+ void initColumn(uint32_t column);
+ void initFunctionDisplayName(JSAtom* maybeName);
+ void initAsyncCause(JSAtom* maybeCause);
+ void initParent(SavedFrame* maybeParent);
+ void initPrincipalsAlreadyHeldAndMutedErrors(JSPrincipals* principals,
+ bool mutedErrors);
+ void initPrincipalsAndMutedErrors(JSPrincipals* principals, bool mutedErrors);
+
+ enum {
+ // The reserved slots in the SavedFrame class.
+ JSSLOT_SOURCE,
+ JSSLOT_SOURCEID,
+ 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 {
+ using Lookup = SavedFrame::Lookup;
+ using SavedFramePtrHasher = StableCellHasher<SavedFrame*>;
+ using JSPrincipalsPtrHasher = PointerHasher<JSPrincipals*>;
+
+ static bool maybeGetHash(const Lookup& l, HashNumber* hashOut);
+ static bool ensureHash(const Lookup& l, HashNumber* hashOut);
+ static HashNumber hash(const Lookup& lookup);
+ static bool match(SavedFrame* existing, const Lookup& lookup);
+
+ using Key = WeakHeapPtr<SavedFrame*>;
+ static void rekey(Key& key, const Key& newKey);
+
+ private:
+ static HashNumber calculateHash(const Lookup& lookup, HashNumber parentHash);
+};
+
+} // namespace js
+
+namespace mozilla {
+
+template <>
+struct FallibleHashMethods<js::SavedFrame::HashPolicy> {
+ template <typename Lookup>
+ static bool maybeGetHash(Lookup&& l, HashNumber* hashOut) {
+ return js::SavedFrame::HashPolicy::maybeGetHash(std::forward<Lookup>(l),
+ hashOut);
+ }
+ template <typename Lookup>
+ static bool ensureHash(Lookup&& l, HashNumber* hashOut) {
+ return js::SavedFrame::HashPolicy::ensureHash(std::forward<Lookup>(l),
+ hashOut);
+ }
+};
+
+} // namespace mozilla
+
+namespace js {
+
+// 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;
+ }
+
+ [[nodiscard]] bool write(JSContext* cx,
+ JSStructuredCloneWriter* writer) override {
+ MOZ_ASSERT(false,
+ "ReconstructedSavedFramePrincipals should never be exposed to "
+ "embedders");
+ return false;
+ }
+
+ bool isSystemOrAddonPrincipal() 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::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);
+ }
+
+ uint32_t sourceId() const override { return get().getSourceId(); }
+
+ 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;
+
+ [[nodiscard]] bool constructSavedFrameStack(
+ JSContext* cx, MutableHandleObject outSavedFrameStack) const override;
+};
+
+} // namespace ubi
+} // namespace JS
+
+#endif // vm_SavedFrame_h