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