SavedFrame.h (10499B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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 #ifndef vm_SavedFrame_h 8 #define vm_SavedFrame_h 9 10 #include "mozilla/Attributes.h" 11 12 #include "gc/Policy.h" 13 #include "js/ColumnNumber.h" // JS::TaggedColumnNumberOneOrigin 14 #include "js/GCHashTable.h" 15 #include "js/Principals.h" 16 #include "js/UbiNode.h" 17 #include "vm/NativeObject.h" 18 19 namespace js { 20 21 class SavedFrame : public NativeObject { 22 friend class SavedStacks; 23 friend struct ::JSStructuredCloneReader; 24 25 static const ClassSpec classSpec_; 26 27 public: 28 static const JSClass class_; 29 static const JSClass protoClass_; 30 static const JSPropertySpec protoAccessors[]; 31 static const JSFunctionSpec protoFunctions[]; 32 static const JSFunctionSpec staticFunctions[]; 33 34 // Prototype methods and properties to be exposed to JS. 35 static bool construct(JSContext* cx, unsigned argc, Value* vp); 36 static bool sourceProperty(JSContext* cx, unsigned argc, Value* vp); 37 static bool sourceIdProperty(JSContext* cx, unsigned argc, Value* vp); 38 static bool lineProperty(JSContext* cx, unsigned argc, Value* vp); 39 static bool columnProperty(JSContext* cx, unsigned argc, Value* vp); 40 static bool functionDisplayNameProperty(JSContext* cx, unsigned argc, 41 Value* vp); 42 static bool asyncCauseProperty(JSContext* cx, unsigned argc, Value* vp); 43 static bool asyncParentProperty(JSContext* cx, unsigned argc, Value* vp); 44 static bool parentProperty(JSContext* cx, unsigned argc, Value* vp); 45 static bool toStringMethod(JSContext* cx, unsigned argc, Value* vp); 46 47 static void finalize(JS::GCContext* gcx, JSObject* obj); 48 49 // Convenient getters for SavedFrame's reserved slots for use from C++. 50 JSAtom* getSource(); 51 uint32_t getSourceId(); 52 // Line number (1-origin). 53 uint32_t getLine(); 54 // Column number in UTF-16 code units. 55 JS::TaggedColumnNumberOneOrigin getColumn(); 56 JSAtom* getFunctionDisplayName(); 57 JSAtom* getAsyncCause(); 58 SavedFrame* getParent() const; 59 JSPrincipals* getPrincipals(); 60 bool getMutedErrors(); 61 bool isSelfHosted(JSContext* cx); 62 bool isWasm(); 63 64 // When isWasm(): 65 uint32_t wasmFuncIndex(); 66 uint32_t wasmBytecodeOffset(); 67 68 // Iterator for use with C++11 range based for loops, eg: 69 // 70 // Rooted<SavedFrame*> stack(cx, getSomeSavedFrameStack()); 71 // for (Handle<SavedFrame*> frame : SavedFrame::RootedRange(cx, stack)) { 72 // ... 73 // } 74 // 75 // Each frame yielded by `SavedFrame::RootedRange` is only a valid handle to 76 // a rooted `SavedFrame` within the loop's block for a single loop 77 // iteration. When the next iteration begins, the value is invalidated. 78 79 class RootedRange; 80 81 class MOZ_STACK_CLASS RootedIterator { 82 friend class RootedRange; 83 RootedRange* range_; 84 // For use by RootedRange::end() only. 85 explicit RootedIterator() : range_(nullptr) {} 86 87 public: 88 explicit RootedIterator(RootedRange& range) : range_(&range) {} 89 Handle<SavedFrame*> operator*() { 90 MOZ_ASSERT(range_); 91 return range_->frame_; 92 } 93 bool operator!=(const RootedIterator& rhs) const { 94 // We should only ever compare to the null range, aka we are just 95 // testing if this range is done. 96 MOZ_ASSERT(rhs.range_ == nullptr); 97 return range_->frame_ != nullptr; 98 } 99 inline void operator++(); 100 }; 101 102 class MOZ_STACK_CLASS RootedRange { 103 friend class RootedIterator; 104 Rooted<SavedFrame*> frame_; 105 106 public: 107 RootedRange(JSContext* cx, Handle<SavedFrame*> frame) : frame_(cx, frame) {} 108 RootedIterator begin() { return RootedIterator(*this); } 109 RootedIterator end() { return RootedIterator(); } 110 }; 111 112 struct Lookup; 113 struct HashPolicy; 114 115 using Set = 116 JS::GCHashSet<WeakHeapPtr<SavedFrame*>, HashPolicy, SystemAllocPolicy>; 117 118 private: 119 static SavedFrame* create(JSContext* cx); 120 [[nodiscard]] static bool finishSavedFrameInit(JSContext* cx, 121 HandleObject ctor, 122 HandleObject proto); 123 void initFromLookup(JSContext* cx, Handle<Lookup> lookup); 124 void initSource(JSAtom* source); 125 void initSourceId(uint32_t id); 126 void initLine(uint32_t line); 127 void initColumn(JS::TaggedColumnNumberOneOrigin column); 128 void initFunctionDisplayName(JSAtom* maybeName); 129 void initAsyncCause(JSAtom* maybeCause); 130 void initParent(SavedFrame* maybeParent); 131 void initPrincipalsAlreadyHeldAndMutedErrors(JSPrincipals* principals, 132 bool mutedErrors); 133 void initPrincipalsAndMutedErrors(JSPrincipals* principals, bool mutedErrors); 134 135 enum { 136 // The reserved slots in the SavedFrame class. 137 JSSLOT_SOURCE, 138 JSSLOT_SOURCEID, 139 JSSLOT_LINE, 140 JSSLOT_COLUMN, 141 JSSLOT_FUNCTIONDISPLAYNAME, 142 JSSLOT_ASYNCCAUSE, 143 JSSLOT_PARENT, 144 JSSLOT_PRINCIPALS, 145 146 // The total number of reserved slots in the SavedFrame class. 147 JSSLOT_COUNT 148 }; 149 }; 150 151 struct SavedFrame::HashPolicy { 152 using Lookup = SavedFrame::Lookup; 153 using SavedFramePtrHasher = StableCellHasher<SavedFrame*>; 154 using JSPrincipalsPtrHasher = PointerHasher<JSPrincipals*>; 155 156 static bool maybeGetHash(const Lookup& l, HashNumber* hashOut); 157 static bool ensureHash(const Lookup& l, HashNumber* hashOut); 158 static HashNumber hash(const Lookup& lookup); 159 static bool match(SavedFrame* existing, const Lookup& lookup); 160 161 using Key = WeakHeapPtr<SavedFrame*>; 162 static void rekey(Key& key, const Key& newKey); 163 164 private: 165 static HashNumber calculateHash(const Lookup& lookup, HashNumber parentHash); 166 }; 167 168 } // namespace js 169 170 namespace mozilla { 171 172 template <> 173 struct FallibleHashMethods<js::SavedFrame::HashPolicy> { 174 template <typename Lookup> 175 static bool maybeGetHash(Lookup&& l, HashNumber* hashOut) { 176 return js::SavedFrame::HashPolicy::maybeGetHash(std::forward<Lookup>(l), 177 hashOut); 178 } 179 template <typename Lookup> 180 static bool ensureHash(Lookup&& l, HashNumber* hashOut) { 181 return js::SavedFrame::HashPolicy::ensureHash(std::forward<Lookup>(l), 182 hashOut); 183 } 184 }; 185 186 } // namespace mozilla 187 188 namespace js { 189 190 // Assert that if the given object is not null, that it must be either a 191 // SavedFrame object or wrapper (Xray or CCW) around a SavedFrame object. 192 inline void AssertObjectIsSavedFrameOrWrapper(JSContext* cx, 193 HandleObject stack); 194 195 // When we reconstruct a SavedFrame stack from a JS::ubi::StackFrame, we may not 196 // have access to the principals that the original stack was captured 197 // with. Instead, we use these two singleton principals based on whether 198 // JS::ubi::StackFrame::isSystem or not. These singletons should never be passed 199 // to the subsumes callback, and should be special cased with a shortcut before 200 // that. 201 struct ReconstructedSavedFramePrincipals : public JSPrincipals { 202 private: 203 explicit constexpr ReconstructedSavedFramePrincipals() 204 : JSPrincipals(JSPrincipals::RefCount(1)) {} 205 206 public: 207 ReconstructedSavedFramePrincipals(const ReconstructedSavedFramePrincipals&) = 208 delete; 209 ReconstructedSavedFramePrincipals& operator=( 210 const ReconstructedSavedFramePrincipals&) = delete; 211 ReconstructedSavedFramePrincipals(ReconstructedSavedFramePrincipals&&) = 212 delete; 213 ReconstructedSavedFramePrincipals& operator=( 214 ReconstructedSavedFramePrincipals&&) = delete; 215 216 [[nodiscard]] bool write(JSContext* cx, 217 JSStructuredCloneWriter* writer) override { 218 MOZ_ASSERT(false, 219 "ReconstructedSavedFramePrincipals should never be exposed to " 220 "embedders"); 221 return false; 222 } 223 224 bool isSystemOrAddonPrincipal() override { 225 MOZ_ASSERT(false, 226 "ReconstructedSavedFramePrincipals should never be exposed to " 227 "embedders"); 228 return false; 229 } 230 231 static ReconstructedSavedFramePrincipals IsSystem; 232 static ReconstructedSavedFramePrincipals IsNotSystem; 233 234 // Return true if the given JSPrincipals* points to one of the 235 // ReconstructedSavedFramePrincipals singletons, false otherwise. 236 static constexpr bool is(JSPrincipals* p) { 237 return p == &IsSystem || p == &IsNotSystem; 238 } 239 240 // Get the appropriate ReconstructedSavedFramePrincipals singleton for the 241 // given JS::ubi::StackFrame that is being reconstructed as a SavedFrame 242 // stack. 243 static JSPrincipals* getSingleton(JS::ubi::StackFrame& f) { 244 return f.isSystem() ? &IsSystem : &IsNotSystem; 245 } 246 }; 247 248 inline void SavedFrame::RootedIterator::operator++() { 249 MOZ_ASSERT(range_); 250 range_->frame_ = range_->frame_->getParent(); 251 } 252 253 } // namespace js 254 255 namespace JS { 256 namespace ubi { 257 258 using js::SavedFrame; 259 260 // A concrete JS::ubi::StackFrame that is backed by a live SavedFrame object. 261 template <> 262 class ConcreteStackFrame<SavedFrame> : public BaseStackFrame { 263 explicit ConcreteStackFrame(SavedFrame* ptr) : BaseStackFrame(ptr) {} 264 SavedFrame& get() const { return *static_cast<SavedFrame*>(ptr); } 265 266 public: 267 static void construct(void* storage, SavedFrame* ptr) { 268 new (storage) ConcreteStackFrame(ptr); 269 } 270 271 StackFrame parent() const override { return get().getParent(); } 272 uint32_t line() const override { return get().getLine(); } 273 JS::TaggedColumnNumberOneOrigin column() const override { 274 return get().getColumn(); 275 } 276 277 AtomOrTwoByteChars source() const override { 278 auto source = get().getSource(); 279 return AtomOrTwoByteChars(source); 280 } 281 282 uint32_t sourceId() const override { return get().getSourceId(); } 283 284 AtomOrTwoByteChars functionDisplayName() const override { 285 auto name = get().getFunctionDisplayName(); 286 return AtomOrTwoByteChars(name); 287 } 288 289 void trace(JSTracer* trc) override { 290 JSObject* prev = &get(); 291 JSObject* next = prev; 292 js::TraceRoot(trc, &next, "ConcreteStackFrame<SavedFrame>::ptr"); 293 if (next != prev) { 294 ptr = next; 295 } 296 } 297 298 bool isSelfHosted(JSContext* cx) const override { 299 return get().isSelfHosted(cx); 300 } 301 302 bool isSystem() const override; 303 304 [[nodiscard]] bool constructSavedFrameStack( 305 JSContext* cx, MutableHandleObject outSavedFrameStack) const override; 306 }; 307 308 } // namespace ubi 309 } // namespace JS 310 311 #endif // vm_SavedFrame_h