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