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 "gc/Tracer.h"
8 :
9 : #include "mozilla/DebugOnly.h"
10 : #include "mozilla/SizePrintfMacros.h"
11 :
12 : #include "jsapi.h"
13 : #include "jsfun.h"
14 : #include "jsgc.h"
15 : #include "jsprf.h"
16 : #include "jsscript.h"
17 : #include "jsutil.h"
18 : #include "NamespaceImports.h"
19 :
20 : #include "gc/GCInternals.h"
21 : #include "gc/Marking.h"
22 : #include "gc/Zone.h"
23 :
24 : #include "vm/Shape.h"
25 : #include "vm/Symbol.h"
26 :
27 : #include "jscompartmentinlines.h"
28 : #include "jsgcinlines.h"
29 :
30 : #include "vm/ObjectGroup-inl.h"
31 :
32 : using namespace js;
33 : using namespace js::gc;
34 : using mozilla::DebugOnly;
35 :
36 : namespace js {
37 : template<typename T>
38 : void
39 : CheckTracedThing(JSTracer* trc, T thing);
40 : } // namespace js
41 :
42 :
43 : /*** Callback Tracer Dispatch ********************************************************************/
44 :
45 : template <typename T>
46 : T
47 5202 : DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name)
48 : {
49 5202 : CheckTracedThing(trc, *thingp);
50 10404 : JS::AutoTracingName ctx(trc, name);
51 5202 : trc->dispatchToOnEdge(thingp);
52 10404 : return *thingp;
53 : }
54 : #define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(name, type, _) \
55 : template type* DoCallback<type*>(JS::CallbackTracer*, type**, const char*);
56 : JS_FOR_EACH_TRACEKIND(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS);
57 : #undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
58 :
59 : template <typename S>
60 : struct DoCallbackFunctor : public IdentityDefaultAdaptor<S> {
61 57 : template <typename T> S operator()(T* t, JS::CallbackTracer* trc, const char* name) {
62 57 : return js::gc::RewrapTaggedPointer<S, T>::wrap(DoCallback(trc, &t, name));
63 : }
64 : };
65 :
66 : template <>
67 : Value
68 3907 : DoCallback<Value>(JS::CallbackTracer* trc, Value* vp, const char* name)
69 : {
70 3907 : *vp = DispatchTyped(DoCallbackFunctor<Value>(), *vp, trc, name);
71 3907 : return *vp;
72 : }
73 :
74 : template <>
75 : jsid
76 0 : DoCallback<jsid>(JS::CallbackTracer* trc, jsid* idp, const char* name)
77 : {
78 0 : *idp = DispatchTyped(DoCallbackFunctor<jsid>(), *idp, trc, name);
79 0 : return *idp;
80 : }
81 :
82 : template <>
83 : TaggedProto
84 0 : DoCallback<TaggedProto>(JS::CallbackTracer* trc, TaggedProto* protop, const char* name)
85 : {
86 0 : *protop = DispatchTyped(DoCallbackFunctor<TaggedProto>(), *protop, trc, name);
87 0 : return *protop;
88 : }
89 :
90 : void
91 0 : JS::CallbackTracer::getTracingEdgeName(char* buffer, size_t bufferSize)
92 : {
93 0 : MOZ_ASSERT(bufferSize > 0);
94 0 : if (contextFunctor_) {
95 0 : (*contextFunctor_)(this, buffer, bufferSize);
96 0 : return;
97 : }
98 0 : if (contextIndex_ != InvalidIndex) {
99 0 : snprintf(buffer, bufferSize, "%s[%" PRIuSIZE "]", contextName_, contextIndex_);
100 0 : return;
101 : }
102 0 : snprintf(buffer, bufferSize, "%s", contextName_);
103 : }
104 :
105 :
106 : /*** Public Tracing API **************************************************************************/
107 :
108 : JS_PUBLIC_API(void)
109 25 : JS::TraceChildren(JSTracer* trc, GCCellPtr thing)
110 : {
111 25 : js::TraceChildren(trc, thing.asCell(), thing.kind());
112 25 : }
113 :
114 : struct TraceChildrenFunctor {
115 : template <typename T>
116 25 : void operator()(JSTracer* trc, void* thingArg) {
117 25 : T* thing = static_cast<T*>(thingArg);
118 25 : MOZ_ASSERT_IF(thing->runtimeFromAnyThread() != trc->runtime(),
119 : ThingIsPermanentAtomOrWellKnownSymbol(thing) ||
120 : thing->zoneFromAnyThread()->isSelfHostingZone());
121 :
122 25 : thing->traceChildren(trc);
123 25 : }
124 : };
125 :
126 : void
127 25 : js::TraceChildren(JSTracer* trc, void* thing, JS::TraceKind kind)
128 : {
129 25 : MOZ_ASSERT(thing);
130 : TraceChildrenFunctor f;
131 25 : DispatchTraceKindTyped(f, kind, trc, thing);
132 25 : }
133 :
134 : namespace {
135 : struct TraceIncomingFunctor {
136 : JSTracer* trc_;
137 : const JS::CompartmentSet& compartments_;
138 0 : TraceIncomingFunctor(JSTracer* trc, const JS::CompartmentSet& compartments)
139 0 : : trc_(trc), compartments_(compartments)
140 0 : {}
141 : template <typename T>
142 0 : void operator()(T tp) {
143 0 : if (!compartments_.has((*tp)->compartment()))
144 0 : return;
145 0 : TraceManuallyBarrieredEdge(trc_, tp, "cross-compartment wrapper");
146 : }
147 : // StringWrappers are just used to avoid copying strings
148 : // across zones multiple times, and don't hold a strong
149 : // reference.
150 0 : void operator()(JSString** tp) {}
151 : };
152 : } // namespace (anonymous)
153 :
154 : JS_PUBLIC_API(void)
155 0 : JS::TraceIncomingCCWs(JSTracer* trc, const JS::CompartmentSet& compartments)
156 : {
157 0 : for (js::CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) {
158 0 : if (compartments.has(comp))
159 0 : continue;
160 :
161 0 : for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) {
162 0 : mozilla::DebugOnly<const CrossCompartmentKey> prior = e.front().key();
163 0 : e.front().mutableKey().applyToWrapped(TraceIncomingFunctor(trc, compartments));
164 0 : MOZ_ASSERT(e.front().key() == prior);
165 : }
166 : }
167 0 : }
168 :
169 :
170 : /*** Cycle Collector Helpers **********************************************************************/
171 :
172 : // This function is used by the Cycle Collector (CC) to trace through -- or in
173 : // CC parlance, traverse -- a Shape tree. The CC does not care about Shapes or
174 : // BaseShapes, only the JSObjects held live by them. Thus, we walk the Shape
175 : // lineage, but only report non-Shape things. This effectively makes the entire
176 : // shape lineage into a single node in the CC, saving tremendous amounts of
177 : // space and time in its algorithms.
178 : //
179 : // The algorithm implemented here uses only bounded stack space. This would be
180 : // possible to implement outside the engine, but would require much extra
181 : // infrastructure and many, many more slow GOT lookups. We have implemented it
182 : // inside SpiderMonkey, despite the lack of general applicability, for the
183 : // simplicity and performance of FireFox's embedding of this engine.
184 : void
185 0 : gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape)
186 : {
187 0 : do {
188 0 : MOZ_ASSERT(shape->base());
189 0 : shape->base()->assertConsistency();
190 :
191 0 : TraceEdge(trc, &shape->propidRef(), "propid");
192 :
193 0 : if (shape->hasGetterObject()) {
194 0 : JSObject* tmp = shape->getterObject();
195 0 : DoCallback(trc, &tmp, "getter");
196 0 : MOZ_ASSERT(tmp == shape->getterObject());
197 : }
198 :
199 0 : if (shape->hasSetterObject()) {
200 0 : JSObject* tmp = shape->setterObject();
201 0 : DoCallback(trc, &tmp, "setter");
202 0 : MOZ_ASSERT(tmp == shape->setterObject());
203 : }
204 :
205 0 : shape = shape->previous();
206 0 : } while (shape);
207 0 : }
208 :
209 : // Object groups can point to other object groups via an UnboxedLayout or the
210 : // the original unboxed group link. There can potentially be deep or cyclic
211 : // chains of such groups to trace through without going through a thing that
212 : // participates in cycle collection. These need to be handled iteratively to
213 : // avoid blowing the stack when running the cycle collector's callback tracer.
214 0 : struct ObjectGroupCycleCollectorTracer : public JS::CallbackTracer
215 : {
216 0 : explicit ObjectGroupCycleCollectorTracer(JS::CallbackTracer* innerTracer)
217 0 : : JS::CallbackTracer(innerTracer->runtime(), DoNotTraceWeakMaps),
218 0 : innerTracer(innerTracer)
219 0 : {}
220 :
221 : void onChild(const JS::GCCellPtr& thing) override;
222 :
223 : JS::CallbackTracer* innerTracer;
224 : Vector<ObjectGroup*, 4, SystemAllocPolicy> seen, worklist;
225 : };
226 :
227 : void
228 0 : ObjectGroupCycleCollectorTracer::onChild(const JS::GCCellPtr& thing)
229 : {
230 0 : if (thing.is<BaseShape>()) {
231 : // The CC does not care about BaseShapes, and no additional GC things
232 : // will be reached by following this edge.
233 0 : return;
234 : }
235 :
236 0 : if (thing.is<JSObject>() || thing.is<JSScript>()) {
237 : // Invoke the inner cycle collector callback on this child. It will not
238 : // recurse back into TraceChildren.
239 0 : innerTracer->onChild(thing);
240 0 : return;
241 : }
242 :
243 0 : if (thing.is<ObjectGroup>()) {
244 : // If this group is required to be in an ObjectGroup chain, trace it
245 : // via the provided worklist rather than continuing to recurse.
246 0 : ObjectGroup& group = thing.as<ObjectGroup>();
247 0 : if (group.maybeUnboxedLayout()) {
248 0 : for (size_t i = 0; i < seen.length(); i++) {
249 0 : if (seen[i] == &group)
250 0 : return;
251 : }
252 0 : if (seen.append(&group) && worklist.append(&group)) {
253 0 : return;
254 : } else {
255 : // If append fails, keep tracing normally. The worst that will
256 : // happen is we end up overrecursing.
257 : }
258 : }
259 : }
260 :
261 0 : TraceChildren(this, thing.asCell(), thing.kind());
262 : }
263 :
264 : void
265 0 : gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group)
266 : {
267 0 : MOZ_ASSERT(trc->isCallbackTracer());
268 :
269 : // Early return if this group is not required to be in an ObjectGroup chain.
270 0 : if (!group->maybeUnboxedLayout())
271 0 : return group->traceChildren(trc);
272 :
273 0 : ObjectGroupCycleCollectorTracer groupTracer(trc->asCallbackTracer());
274 0 : group->traceChildren(&groupTracer);
275 :
276 0 : while (!groupTracer.worklist.empty()) {
277 0 : ObjectGroup* innerGroup = groupTracer.worklist.popCopy();
278 0 : innerGroup->traceChildren(&groupTracer);
279 : }
280 : }
281 :
282 :
283 : /*** Traced Edge Printer *************************************************************************/
284 :
285 : static size_t
286 0 : CountDecimalDigits(size_t num)
287 : {
288 0 : size_t numDigits = 0;
289 0 : do {
290 0 : num /= 10;
291 0 : numDigits++;
292 0 : } while (num > 0);
293 :
294 0 : return numDigits;
295 : }
296 :
297 : static const char*
298 0 : StringKindHeader(JSString* str)
299 : {
300 0 : MOZ_ASSERT(str->isLinear());
301 :
302 0 : if (str->isAtom()) {
303 0 : if (str->isPermanentAtom())
304 0 : return "permanent atom: ";
305 0 : return "atom: ";
306 : }
307 :
308 0 : if (str->isFlat()) {
309 0 : if (str->isExtensible())
310 0 : return "extensible: ";
311 0 : if (str->isUndepended())
312 0 : return "undepended: ";
313 0 : if (str->isInline()) {
314 0 : if (str->isFatInline())
315 0 : return "fat inline: ";
316 0 : return "inline: ";
317 : }
318 0 : return "flat: ";
319 : }
320 :
321 0 : if (str->isDependent())
322 0 : return "dependent: ";
323 0 : if (str->isExternal())
324 0 : return "external: ";
325 0 : return "linear: ";
326 : }
327 :
328 : JS_PUBLIC_API(void)
329 0 : JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, void* thing,
330 : JS::TraceKind kind, bool details)
331 : {
332 0 : const char* name = nullptr; /* silence uninitialized warning */
333 : size_t n;
334 :
335 0 : if (bufsize == 0)
336 0 : return;
337 :
338 0 : switch (kind) {
339 : case JS::TraceKind::BaseShape:
340 0 : name = "base_shape";
341 0 : break;
342 :
343 : case JS::TraceKind::JitCode:
344 0 : name = "jitcode";
345 0 : break;
346 :
347 : case JS::TraceKind::LazyScript:
348 0 : name = "lazyscript";
349 0 : break;
350 :
351 : case JS::TraceKind::Null:
352 0 : name = "null_pointer";
353 0 : break;
354 :
355 : case JS::TraceKind::Object:
356 : {
357 0 : name = static_cast<JSObject*>(thing)->getClass()->name;
358 0 : break;
359 : }
360 :
361 : case JS::TraceKind::ObjectGroup:
362 0 : name = "object_group";
363 0 : break;
364 :
365 : case JS::TraceKind::RegExpShared:
366 0 : name = "reg_exp_shared";
367 0 : break;
368 :
369 : case JS::TraceKind::Scope:
370 0 : name = "scope";
371 0 : break;
372 :
373 : case JS::TraceKind::Script:
374 0 : name = "script";
375 0 : break;
376 :
377 : case JS::TraceKind::Shape:
378 0 : name = "shape";
379 0 : break;
380 :
381 : case JS::TraceKind::String:
382 0 : name = ((JSString*)thing)->isDependent()
383 0 : ? "substring"
384 : : "string";
385 0 : break;
386 :
387 : case JS::TraceKind::Symbol:
388 0 : name = "symbol";
389 0 : break;
390 :
391 : default:
392 0 : name = "INVALID";
393 0 : break;
394 : }
395 :
396 0 : n = strlen(name);
397 0 : if (n > bufsize - 1)
398 0 : n = bufsize - 1;
399 0 : js_memcpy(buf, name, n + 1);
400 0 : buf += n;
401 0 : bufsize -= n;
402 0 : *buf = '\0';
403 :
404 0 : if (details && bufsize > 2) {
405 0 : switch (kind) {
406 : case JS::TraceKind::Object:
407 : {
408 0 : JSObject* obj = (JSObject*)thing;
409 0 : if (obj->is<JSFunction>()) {
410 0 : JSFunction* fun = &obj->as<JSFunction>();
411 0 : if (fun->displayAtom()) {
412 0 : *buf++ = ' ';
413 0 : bufsize--;
414 0 : PutEscapedString(buf, bufsize, fun->displayAtom(), 0);
415 : }
416 0 : } else if (obj->getClass()->flags & JSCLASS_HAS_PRIVATE) {
417 0 : snprintf(buf, bufsize, " %p", obj->as<NativeObject>().getPrivate());
418 : } else {
419 0 : snprintf(buf, bufsize, " <no private>");
420 : }
421 0 : break;
422 : }
423 :
424 : case JS::TraceKind::Script:
425 : {
426 0 : JSScript* script = static_cast<JSScript*>(thing);
427 0 : snprintf(buf, bufsize, " %s:%" PRIuSIZE, script->filename(), script->lineno());
428 0 : break;
429 : }
430 :
431 : case JS::TraceKind::String:
432 : {
433 0 : *buf++ = ' ';
434 0 : bufsize--;
435 0 : JSString* str = (JSString*)thing;
436 :
437 0 : if (str->isLinear()) {
438 0 : const char* header = StringKindHeader(str);
439 0 : bool willFit = str->length() + strlen("<length > ") + strlen(header) +
440 0 : CountDecimalDigits(str->length()) < bufsize;
441 :
442 0 : n = snprintf(buf, bufsize, "<%slength %" PRIuSIZE "%s> ",
443 : header, str->length(),
444 : willFit ? "" : " (truncated)");
445 0 : buf += n;
446 0 : bufsize -= n;
447 :
448 0 : PutEscapedString(buf, bufsize, &str->asLinear(), 0);
449 : } else {
450 0 : snprintf(buf, bufsize, "<rope: length %" PRIuSIZE ">", str->length());
451 : }
452 0 : break;
453 : }
454 :
455 : case JS::TraceKind::Symbol:
456 : {
457 0 : JS::Symbol* sym = static_cast<JS::Symbol*>(thing);
458 0 : if (JSString* desc = sym->description()) {
459 0 : if (desc->isLinear()) {
460 0 : *buf++ = ' ';
461 0 : bufsize--;
462 0 : PutEscapedString(buf, bufsize, &desc->asLinear(), 0);
463 : } else {
464 0 : snprintf(buf, bufsize, "<nonlinear desc>");
465 : }
466 : } else {
467 0 : snprintf(buf, bufsize, "<null>");
468 : }
469 0 : break;
470 : }
471 :
472 : case JS::TraceKind::Scope:
473 : {
474 0 : js::Scope* scope = static_cast<js::Scope*>(thing);
475 0 : snprintf(buf, bufsize, " %s", js::ScopeKindString(scope->kind()));
476 0 : break;
477 : }
478 :
479 : default:
480 0 : break;
481 : }
482 : }
483 0 : buf[bufsize - 1] = '\0';
484 : }
485 :
486 25 : JS::CallbackTracer::CallbackTracer(JSContext* cx, WeakMapTraceKind weakTraceKind)
487 25 : : CallbackTracer(cx->runtime(), weakTraceKind)
488 25 : {}
|