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 : #include "js/UbiNode.h"
8 :
9 : #include "mozilla/Assertions.h"
10 : #include "mozilla/Attributes.h"
11 : #include "mozilla/Range.h"
12 : #include "mozilla/Scoped.h"
13 :
14 : #include <algorithm>
15 :
16 : #include "jscntxt.h"
17 : #include "jsobj.h"
18 : #include "jsscript.h"
19 : #include "jsstr.h"
20 :
21 : #include "jit/IonCode.h"
22 : #include "js/Debug.h"
23 : #include "js/TracingAPI.h"
24 : #include "js/TypeDecls.h"
25 : #include "js/Utility.h"
26 : #include "js/Vector.h"
27 : #include "vm/Debugger.h"
28 : #include "vm/EnvironmentObject.h"
29 : #include "vm/GlobalObject.h"
30 : #include "vm/Scope.h"
31 : #include "vm/Shape.h"
32 : #include "vm/String.h"
33 : #include "vm/Symbol.h"
34 :
35 : #include "jsobjinlines.h"
36 : #include "vm/Debugger-inl.h"
37 :
38 : using namespace js;
39 :
40 : using mozilla::Some;
41 : using mozilla::RangedPtr;
42 : using JS::DispatchTyped;
43 : using JS::HandleValue;
44 : using JS::Value;
45 : using JS::ZoneSet;
46 : using JS::ubi::AtomOrTwoByteChars;
47 : using JS::ubi::CoarseType;
48 : using JS::ubi::Concrete;
49 : using JS::ubi::Edge;
50 : using JS::ubi::EdgeRange;
51 : using JS::ubi::Node;
52 : using JS::ubi::EdgeVector;
53 : using JS::ubi::StackFrame;
54 : using JS::ubi::TracerConcrete;
55 : using JS::ubi::TracerConcreteWithCompartment;
56 :
57 : struct CopyToBufferMatcher
58 : {
59 : RangedPtr<char16_t> destination;
60 : size_t maxLength;
61 :
62 0 : CopyToBufferMatcher(RangedPtr<char16_t> destination, size_t maxLength)
63 0 : : destination(destination)
64 0 : , maxLength(maxLength)
65 0 : { }
66 :
67 : template<typename CharT>
68 : static size_t
69 0 : copyToBufferHelper(const CharT* src, RangedPtr<char16_t> dest, size_t length)
70 : {
71 0 : size_t i = 0;
72 0 : for ( ; i < length; i++)
73 0 : dest[i] = src[i];
74 0 : return i;
75 : }
76 :
77 : size_t
78 0 : match(JSAtom* atom)
79 : {
80 0 : if (!atom)
81 0 : return 0;
82 :
83 0 : size_t length = std::min(atom->length(), maxLength);
84 0 : JS::AutoCheckCannotGC noGC;
85 0 : return atom->hasTwoByteChars()
86 0 : ? copyToBufferHelper(atom->twoByteChars(noGC), destination, length)
87 0 : : copyToBufferHelper(atom->latin1Chars(noGC), destination, length);
88 : }
89 :
90 : size_t
91 0 : match(const char16_t* chars)
92 : {
93 0 : if (!chars)
94 0 : return 0;
95 :
96 0 : size_t length = std::min(js_strlen(chars), maxLength);
97 0 : return copyToBufferHelper(chars, destination, length);
98 : }
99 : };
100 :
101 : size_t
102 0 : JS::ubi::AtomOrTwoByteChars::copyToBuffer(RangedPtr<char16_t> destination, size_t length)
103 : {
104 0 : CopyToBufferMatcher m(destination, length);
105 0 : return match(m);
106 : }
107 :
108 : struct LengthMatcher
109 : {
110 : size_t
111 0 : match(JSAtom* atom)
112 : {
113 0 : return atom ? atom->length() : 0;
114 : }
115 :
116 : size_t
117 0 : match(const char16_t* chars)
118 : {
119 0 : return chars ? js_strlen(chars) : 0;
120 : }
121 : };
122 :
123 : size_t
124 0 : JS::ubi::AtomOrTwoByteChars::length()
125 : {
126 : LengthMatcher m;
127 0 : return match(m);
128 : }
129 :
130 : size_t
131 0 : StackFrame::source(RangedPtr<char16_t> destination, size_t length) const
132 : {
133 0 : auto s = source();
134 0 : return s.copyToBuffer(destination, length);
135 : }
136 :
137 : size_t
138 0 : StackFrame::functionDisplayName(RangedPtr<char16_t> destination, size_t length) const
139 : {
140 0 : auto name = functionDisplayName();
141 0 : return name.copyToBuffer(destination, length);
142 : }
143 :
144 : size_t
145 0 : StackFrame::sourceLength()
146 : {
147 0 : return source().length();
148 : }
149 :
150 : size_t
151 0 : StackFrame::functionDisplayNameLength()
152 : {
153 0 : return functionDisplayName().length();
154 : }
155 :
156 : // All operations on null ubi::Nodes crash.
157 0 : CoarseType Concrete<void>::coarseType() const { MOZ_CRASH("null ubi::Node"); }
158 0 : const char16_t* Concrete<void>::typeName() const { MOZ_CRASH("null ubi::Node"); }
159 0 : JS::Zone* Concrete<void>::zone() const { MOZ_CRASH("null ubi::Node"); }
160 0 : JSCompartment* Concrete<void>::compartment() const { MOZ_CRASH("null ubi::Node"); }
161 :
162 : UniquePtr<EdgeRange>
163 0 : Concrete<void>::edges(JSContext*, bool) const {
164 0 : MOZ_CRASH("null ubi::Node");
165 : }
166 :
167 : Node::Size
168 0 : Concrete<void>::size(mozilla::MallocSizeOf mallocSizeof) const
169 : {
170 0 : MOZ_CRASH("null ubi::Node");
171 : }
172 :
173 : struct Node::ConstructFunctor : public js::BoolDefaultAdaptor<Value, false> {
174 0 : template <typename T> bool operator()(T* t, Node* node) { node->construct(t); return true; }
175 : };
176 :
177 0 : Node::Node(const JS::GCCellPtr &thing)
178 : {
179 0 : DispatchTyped(ConstructFunctor(), thing, this);
180 0 : }
181 :
182 0 : Node::Node(HandleValue value)
183 : {
184 0 : if (!DispatchTyped(ConstructFunctor(), value, this))
185 0 : construct<void>(nullptr);
186 0 : }
187 :
188 : Value
189 0 : Node::exposeToJS() const
190 : {
191 0 : Value v;
192 :
193 0 : if (is<JSObject>()) {
194 0 : JSObject& obj = *as<JSObject>();
195 0 : if (obj.is<js::EnvironmentObject>()) {
196 0 : v.setUndefined();
197 0 : } else if (obj.is<JSFunction>() && js::IsInternalFunctionObject(obj)) {
198 0 : v.setUndefined();
199 : } else {
200 0 : v.setObject(obj);
201 : }
202 0 : } else if (is<JSString>()) {
203 0 : v.setString(as<JSString>());
204 0 : } else if (is<JS::Symbol>()) {
205 0 : v.setSymbol(as<JS::Symbol>());
206 : } else {
207 0 : v.setUndefined();
208 : }
209 :
210 0 : ExposeValueToActiveJS(v);
211 :
212 0 : return v;
213 : }
214 :
215 :
216 : // A JS::CallbackTracer subclass that adds a Edge to a Vector for each
217 : // edge on which it is invoked.
218 : class EdgeVectorTracer : public JS::CallbackTracer {
219 : // The vector to which we add Edges.
220 : EdgeVector* vec;
221 :
222 : // True if we should populate the edge's names.
223 : bool wantNames;
224 :
225 0 : void onChild(const JS::GCCellPtr& thing) override {
226 0 : if (!okay)
227 0 : return;
228 :
229 : // Don't trace permanent atoms and well-known symbols that are owned by
230 : // a parent JSRuntime.
231 0 : if (thing.is<JSString>() && thing.as<JSString>().isPermanentAtom())
232 0 : return;
233 0 : if (thing.is<JS::Symbol>() && thing.as<JS::Symbol>().isWellKnownSymbol())
234 0 : return;
235 :
236 0 : char16_t* name16 = nullptr;
237 0 : if (wantNames) {
238 : // Ask the tracer to compute an edge name for us.
239 : char buffer[1024];
240 0 : getTracingEdgeName(buffer, sizeof(buffer));
241 0 : const char* name = buffer;
242 :
243 : // Convert the name to char16_t characters.
244 0 : name16 = js_pod_malloc<char16_t>(strlen(name) + 1);
245 0 : if (!name16) {
246 0 : okay = false;
247 0 : return;
248 : }
249 :
250 : size_t i;
251 0 : for (i = 0; name[i]; i++)
252 0 : name16[i] = name[i];
253 0 : name16[i] = '\0';
254 : }
255 :
256 : // The simplest code is correct! The temporary Edge takes
257 : // ownership of name; if the append succeeds, the vector element
258 : // then takes ownership; if the append fails, then the temporary
259 : // retains it, and its destructor will free it.
260 0 : if (!vec->append(mozilla::Move(Edge(name16, Node(thing))))) {
261 0 : okay = false;
262 0 : return;
263 : }
264 : }
265 :
266 : public:
267 : // True if no errors (OOM, say) have yet occurred.
268 : bool okay;
269 :
270 0 : EdgeVectorTracer(JSRuntime* rt, EdgeVector* vec, bool wantNames)
271 0 : : JS::CallbackTracer(rt),
272 : vec(vec),
273 : wantNames(wantNames),
274 0 : okay(true)
275 0 : { }
276 : };
277 :
278 :
279 : // An EdgeRange concrete class that simply holds a vector of Edges,
280 : // populated by the init method.
281 0 : class SimpleEdgeRange : public EdgeRange {
282 : EdgeVector edges;
283 : size_t i;
284 :
285 0 : void settle() {
286 0 : front_ = i < edges.length() ? &edges[i] : nullptr;
287 0 : }
288 :
289 : public:
290 0 : explicit SimpleEdgeRange() : edges(), i(0) { }
291 :
292 0 : bool init(JSRuntime* rt, void* thing, JS::TraceKind kind, bool wantNames = true) {
293 0 : EdgeVectorTracer tracer(rt, &edges, wantNames);
294 0 : js::TraceChildren(&tracer, thing, kind);
295 0 : settle();
296 0 : return tracer.okay;
297 : }
298 :
299 0 : void popFront() override { i++; settle(); }
300 : };
301 :
302 :
303 : template<typename Referent>
304 : JS::Zone*
305 0 : TracerConcrete<Referent>::zone() const
306 : {
307 0 : return get().zoneFromAnyThread();
308 : }
309 :
310 : template JS::Zone* TracerConcrete<JSScript>::zone() const;
311 : template JS::Zone* TracerConcrete<js::LazyScript>::zone() const;
312 : template JS::Zone* TracerConcrete<js::Shape>::zone() const;
313 : template JS::Zone* TracerConcrete<js::BaseShape>::zone() const;
314 : template JS::Zone* TracerConcrete<js::ObjectGroup>::zone() const;
315 : template JS::Zone* TracerConcrete<js::RegExpShared>::zone() const;
316 : template JS::Zone* TracerConcrete<js::Scope>::zone() const;
317 : template JS::Zone* TracerConcrete<JS::Symbol>::zone() const;
318 : template JS::Zone* TracerConcrete<JSString>::zone() const;
319 :
320 : template<typename Referent>
321 : UniquePtr<EdgeRange>
322 0 : TracerConcrete<Referent>::edges(JSContext* cx, bool wantNames) const {
323 0 : UniquePtr<SimpleEdgeRange, JS::DeletePolicy<SimpleEdgeRange>> range(js_new<SimpleEdgeRange>());
324 0 : if (!range)
325 0 : return nullptr;
326 :
327 0 : if (!range->init(cx->runtime(), ptr, JS::MapTypeToTraceKind<Referent>::kind, wantNames))
328 0 : return nullptr;
329 :
330 0 : return UniquePtr<EdgeRange>(range.release());
331 : }
332 :
333 : template UniquePtr<EdgeRange> TracerConcrete<JSScript>::edges(JSContext* cx, bool wantNames) const;
334 : template UniquePtr<EdgeRange> TracerConcrete<js::LazyScript>::edges(JSContext* cx, bool wantNames) const;
335 : template UniquePtr<EdgeRange> TracerConcrete<js::Shape>::edges(JSContext* cx, bool wantNames) const;
336 : template UniquePtr<EdgeRange> TracerConcrete<js::BaseShape>::edges(JSContext* cx, bool wantNames) const;
337 : template UniquePtr<EdgeRange> TracerConcrete<js::ObjectGroup>::edges(JSContext* cx, bool wantNames) const;
338 : template UniquePtr<EdgeRange> TracerConcrete<js::RegExpShared>::edges(JSContext* cx, bool wantNames) const;
339 : template UniquePtr<EdgeRange> TracerConcrete<js::Scope>::edges(JSContext* cx, bool wantNames) const;
340 : template UniquePtr<EdgeRange> TracerConcrete<JS::Symbol>::edges(JSContext* cx, bool wantNames) const;
341 : template UniquePtr<EdgeRange> TracerConcrete<JSString>::edges(JSContext* cx, bool wantNames) const;
342 :
343 : template<typename Referent>
344 : JSCompartment*
345 0 : TracerConcreteWithCompartment<Referent>::compartment() const
346 : {
347 0 : return TracerBase::get().compartment();
348 : }
349 :
350 : template JSCompartment* TracerConcreteWithCompartment<JSScript>::compartment() const;
351 :
352 : bool
353 0 : Concrete<JSObject>::hasAllocationStack() const
354 : {
355 0 : return !!js::Debugger::getObjectAllocationSite(get());
356 : }
357 :
358 : StackFrame
359 0 : Concrete<JSObject>::allocationStack() const
360 : {
361 0 : MOZ_ASSERT(hasAllocationStack());
362 0 : return StackFrame(js::Debugger::getObjectAllocationSite(get()));
363 : }
364 :
365 : const char*
366 0 : Concrete<JSObject>::jsObjectClassName() const
367 : {
368 0 : return Concrete::get().getClass()->name;
369 : }
370 :
371 : bool
372 0 : Concrete<JSObject>::jsObjectConstructorName(JSContext* cx, UniqueTwoByteChars& outName) const
373 : {
374 0 : JSAtom* name = Concrete::get().maybeConstructorDisplayAtom();
375 0 : if (!name) {
376 0 : outName.reset(nullptr);
377 0 : return true;
378 : }
379 :
380 0 : auto len = JS_GetStringLength(name);
381 0 : auto size = len + 1;
382 :
383 0 : outName.reset(cx->pod_malloc<char16_t>(size * sizeof(char16_t)));
384 0 : if (!outName)
385 0 : return false;
386 :
387 0 : mozilla::Range<char16_t> chars(outName.get(), size);
388 0 : if (!JS_CopyStringChars(cx, chars, name))
389 0 : return false;
390 :
391 0 : outName[len] = '\0';
392 0 : return true;
393 : }
394 :
395 : const char16_t Concrete<JS::Symbol>::concreteTypeName[] = u"JS::Symbol";
396 : const char16_t Concrete<JSScript>::concreteTypeName[] = u"JSScript";
397 : const char16_t Concrete<js::LazyScript>::concreteTypeName[] = u"js::LazyScript";
398 : const char16_t Concrete<js::jit::JitCode>::concreteTypeName[] = u"js::jit::JitCode";
399 : const char16_t Concrete<js::Shape>::concreteTypeName[] = u"js::Shape";
400 : const char16_t Concrete<js::BaseShape>::concreteTypeName[] = u"js::BaseShape";
401 : const char16_t Concrete<js::ObjectGroup>::concreteTypeName[] = u"js::ObjectGroup";
402 : const char16_t Concrete<js::Scope>::concreteTypeName[] = u"js::Scope";
403 : const char16_t Concrete<js::RegExpShared>::concreteTypeName[] = u"js::RegExpShared";
404 :
405 : namespace JS {
406 : namespace ubi {
407 :
408 0 : RootList::RootList(JSContext* cx, Maybe<AutoCheckCannotGC>& noGC, bool wantNames /* = false */)
409 : : noGC(noGC),
410 : cx(cx),
411 : edges(),
412 0 : wantNames(wantNames)
413 0 : { }
414 :
415 :
416 : bool
417 0 : RootList::init()
418 : {
419 0 : EdgeVectorTracer tracer(cx->runtime(), &edges, wantNames);
420 0 : js::TraceRuntime(&tracer);
421 0 : if (!tracer.okay)
422 0 : return false;
423 0 : noGC.emplace();
424 0 : return true;
425 : }
426 :
427 : bool
428 0 : RootList::init(CompartmentSet& debuggees)
429 : {
430 0 : EdgeVector allRootEdges;
431 0 : EdgeVectorTracer tracer(cx->runtime(), &allRootEdges, wantNames);
432 :
433 0 : ZoneSet debuggeeZones;
434 0 : if (!debuggeeZones.init())
435 0 : return false;
436 0 : for (auto range = debuggees.all(); !range.empty(); range.popFront()) {
437 0 : if (!debuggeeZones.put(range.front()->zone()))
438 0 : return false;
439 : }
440 :
441 0 : js::TraceRuntime(&tracer);
442 0 : if (!tracer.okay)
443 0 : return false;
444 0 : TraceIncomingCCWs(&tracer, debuggees);
445 0 : if (!tracer.okay)
446 0 : return false;
447 :
448 0 : for (EdgeVector::Range r = allRootEdges.all(); !r.empty(); r.popFront()) {
449 0 : Edge& edge = r.front();
450 :
451 0 : JSCompartment* compartment = edge.referent.compartment();
452 0 : if (compartment && !debuggees.has(compartment))
453 0 : continue;
454 :
455 0 : Zone* zone = edge.referent.zone();
456 0 : if (zone && !debuggeeZones.has(zone))
457 0 : continue;
458 :
459 0 : if (!edges.append(mozilla::Move(edge)))
460 0 : return false;
461 : }
462 :
463 0 : noGC.emplace();
464 0 : return true;
465 : }
466 :
467 : bool
468 0 : RootList::init(HandleObject debuggees)
469 : {
470 0 : MOZ_ASSERT(debuggees && JS::dbg::IsDebugger(*debuggees));
471 0 : js::Debugger* dbg = js::Debugger::fromJSObject(debuggees.get());
472 :
473 0 : CompartmentSet debuggeeCompartments;
474 0 : if (!debuggeeCompartments.init())
475 0 : return false;
476 :
477 0 : for (js::WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
478 0 : if (!debuggeeCompartments.put(r.front()->compartment()))
479 0 : return false;
480 : }
481 :
482 0 : if (!init(debuggeeCompartments))
483 0 : return false;
484 :
485 : // Ensure that each of our debuggee globals are in the root list.
486 0 : for (js::WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
487 0 : if (!addRoot(JS::ubi::Node(static_cast<JSObject*>(r.front())),
488 : u"debuggee global"))
489 : {
490 0 : return false;
491 : }
492 : }
493 :
494 0 : return true;
495 : }
496 :
497 : bool
498 0 : RootList::addRoot(Node node, const char16_t* edgeName)
499 : {
500 0 : MOZ_ASSERT(noGC.isSome());
501 0 : MOZ_ASSERT_IF(wantNames, edgeName);
502 :
503 0 : UniqueTwoByteChars name;
504 0 : if (edgeName) {
505 0 : name = js::DuplicateString(edgeName);
506 0 : if (!name)
507 0 : return false;
508 : }
509 :
510 0 : return edges.append(mozilla::Move(Edge(name.release(), node)));
511 : }
512 :
513 : const char16_t Concrete<RootList>::concreteTypeName[] = u"JS::ubi::RootList";
514 :
515 : UniquePtr<EdgeRange>
516 0 : Concrete<RootList>::edges(JSContext* cx, bool wantNames) const {
517 0 : MOZ_ASSERT_IF(wantNames, get().wantNames);
518 0 : return UniquePtr<EdgeRange>(js_new<PreComputedEdgeRange>(get().edges));
519 : }
520 :
521 : } // namespace ubi
522 : } // namespace JS
|