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