1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : */
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 file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "jsgcmark.h"
8 : #include "jsprf.h"
9 : #include "jsscope.h"
10 : #include "jsstr.h"
11 :
12 : #include "jsobjinlines.h"
13 : #include "jsscopeinlines.h"
14 :
15 : #include "vm/String-inl.h"
16 : #include "methodjit/MethodJIT.h"
17 :
18 : /*
19 : * There are two mostly separate mark paths. The first is a fast path used
20 : * internally in the GC. The second is a slow path used for root marking and
21 : * for API consumers like the cycle collector or Class::trace implementations.
22 : *
23 : * The fast path uses explicit stacks. The basic marking process during a GC is
24 : * that all roots are pushed on to a mark stack, and then each item on the
25 : * stack is scanned (possibly pushing more stuff) until the stack is empty.
26 : *
27 : * PushMarkStack pushes a GC thing onto the mark stack. In some cases (shapes
28 : * or strings) it eagerly marks the object rather than pushing it. Popping and
29 : * scanning is done by the processMarkStackTop method. For efficiency reasons
30 : * like tail recursion elimination that method also implements the scanning of
31 : * objects. For other GC things it uses helper methods.
32 : *
33 : * Most of the marking code outside jsgcmark uses functions like MarkObject,
34 : * MarkString, etc. These functions check if an object is in the compartment
35 : * currently being GCed. If it is, they call PushMarkStack. Roots are pushed
36 : * this way as well as pointers traversed inside trace hooks (for things like
37 : * IteratorClass). It it always valid to call a MarkX function instead of
38 : * PushMarkStack, although it may be slower.
39 : *
40 : * The MarkX functions also handle non-GC object traversal. In this case, they
41 : * call a callback for each object visited. This is a recursive process; the
42 : * mark stacks are not involved. These callbacks may ask for the outgoing
43 : * pointers to be visited. Eventually, this leads to the MarkChildren functions
44 : * being called. These functions duplicate much of the functionality of
45 : * scanning functions, but they don't push onto an explicit stack.
46 : */
47 :
48 : namespace js {
49 : namespace gc {
50 :
51 : static inline void
52 : PushMarkStack(GCMarker *gcmarker, JSXML *thing);
53 :
54 : static inline void
55 : PushMarkStack(GCMarker *gcmarker, JSObject *thing);
56 :
57 : static inline void
58 : PushMarkStack(GCMarker *gcmarker, JSFunction *thing);
59 :
60 : static inline void
61 : PushMarkStack(GCMarker *gcmarker, JSScript *thing);
62 :
63 : static inline void
64 : PushMarkStack(GCMarker *gcmarker, Shape *thing);
65 :
66 : static inline void
67 : PushMarkStack(GCMarker *gcmarker, JSString *thing);
68 :
69 : static inline void
70 : PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing);
71 :
72 : /*** Object Marking ***/
73 :
74 : template<typename T>
75 : static inline void
76 263204738 : CheckMarkedThing(JSTracer *trc, T *thing)
77 : {
78 263204738 : JS_ASSERT(trc);
79 263204738 : JS_ASSERT(thing);
80 263204738 : JS_ASSERT(thing->compartment());
81 263204738 : JS_ASSERT(thing->compartment()->rt == trc->runtime);
82 263204738 : JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);
83 :
84 526409476 : DebugOnly<JSRuntime *> rt = trc->runtime;
85 :
86 263204738 : JS_ASSERT_IF(thing->compartment()->requireGCTracer(), IS_GC_MARKING_TRACER(trc));
87 :
88 263204738 : JS_ASSERT(thing->isAligned());
89 :
90 263204738 : JS_ASSERT_IF(rt->gcStrictCompartmentChecking,
91 : thing->compartment()->isCollecting() ||
92 : thing->compartment() == rt->atomsCompartment);
93 263204738 : }
94 :
95 : template<typename T>
96 : void
97 263204738 : MarkInternal(JSTracer *trc, T **thingp)
98 : {
99 263204738 : JS_ASSERT(thingp);
100 263204738 : T *thing = *thingp;
101 :
102 263204738 : CheckMarkedThing(trc, thing);
103 :
104 : /*
105 : * Don't mark things outside a compartment if we are in a per-compartment
106 : * GC.
107 : */
108 263204738 : if (!trc->callback) {
109 222684879 : if (thing->compartment()->isCollecting())
110 221754708 : PushMarkStack(static_cast<GCMarker *>(trc), thing);
111 : } else {
112 40519859 : trc->callback(trc, (void **)thingp, GetGCThingTraceKind(thing));
113 : }
114 :
115 : #ifdef DEBUG
116 263204738 : trc->debugPrinter = NULL;
117 263204738 : trc->debugPrintArg = NULL;
118 : #endif
119 263204738 : }
120 :
121 : #define JS_ROOT_MARKING_ASSERT(trc) \
122 : JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc), \
123 : trc->runtime->gcIncrementalState == NO_INCREMENTAL || \
124 : trc->runtime->gcIncrementalState == MARK_ROOTS);
125 :
126 :
127 : template <typename T>
128 : static void
129 196762884 : MarkUnbarriered(JSTracer *trc, T **thingp, const char *name)
130 : {
131 196762884 : JS_SET_TRACING_NAME(trc, name);
132 196762884 : MarkInternal(trc, thingp);
133 196762884 : }
134 :
135 : template <typename T>
136 : static void
137 33959951 : Mark(JSTracer *trc, HeapPtr<T> *thing, const char *name)
138 : {
139 33959951 : JS_SET_TRACING_NAME(trc, name);
140 33959951 : MarkInternal(trc, thing->unsafeGet());
141 33959951 : }
142 :
143 : template <typename T>
144 : static void
145 11002695 : MarkRoot(JSTracer *trc, T **thingp, const char *name)
146 : {
147 11002695 : JS_ROOT_MARKING_ASSERT(trc);
148 11002695 : JS_SET_TRACING_NAME(trc, name);
149 11002695 : MarkInternal(trc, thingp);
150 11002695 : }
151 :
152 : template <typename T>
153 : static void
154 26674 : MarkRange(JSTracer *trc, size_t len, HeapPtr<T> *vec, const char *name)
155 : {
156 73620 : for (size_t i = 0; i < len; ++i) {
157 46946 : if (vec[i].get()) {
158 46946 : JS_SET_TRACING_INDEX(trc, name, i);
159 46946 : MarkInternal(trc, vec[i].unsafeGet());
160 : }
161 : }
162 26674 : }
163 :
164 : template <typename T>
165 : static void
166 1107 : MarkRootRange(JSTracer *trc, size_t len, T **vec, const char *name)
167 : {
168 1107 : JS_ROOT_MARKING_ASSERT(trc);
169 7388 : for (size_t i = 0; i < len; ++i) {
170 6281 : JS_SET_TRACING_INDEX(trc, name, i);
171 6281 : MarkInternal(trc, &vec[i]);
172 : }
173 1107 : }
174 :
175 : #define DeclMarkerImpl(base, type) \
176 : void \
177 : Mark##base(JSTracer *trc, HeapPtr<type> *thing, const char *name) \
178 : { \
179 : Mark<type>(trc, thing, name); \
180 : } \
181 : \
182 : void \
183 : Mark##base##Root(JSTracer *trc, type **thingp, const char *name) \
184 : { \
185 : MarkRoot<type>(trc, thingp, name); \
186 : } \
187 : \
188 : void \
189 : Mark##base##Unbarriered(JSTracer *trc, type **thingp, const char *name) \
190 : { \
191 : MarkUnbarriered<type>(trc, thingp, name); \
192 : } \
193 : \
194 : void Mark##base##Range(JSTracer *trc, size_t len, HeapPtr<type> *vec, const char *name) \
195 : { \
196 : MarkRange<type>(trc, len, vec, name); \
197 : } \
198 : \
199 : void Mark##base##RootRange(JSTracer *trc, size_t len, type **vec, const char *name) \
200 : { \
201 : MarkRootRange<type>(trc, len, vec, name); \
202 : } \
203 :
204 3673328 : DeclMarkerImpl(BaseShape, BaseShape)
205 34789 : DeclMarkerImpl(BaseShape, UnownedBaseShape)
206 1182 : DeclMarkerImpl(Object, ArgumentsObject)
207 67363 : DeclMarkerImpl(Object, GlobalObject)
208 3124747 : DeclMarkerImpl(Object, JSObject)
209 697116 : DeclMarkerImpl(Object, JSFunction)
210 979079 : DeclMarkerImpl(Script, JSScript)
211 6172615 : DeclMarkerImpl(Shape, Shape)
212 215367744 : DeclMarkerImpl(String, JSAtom)
213 139350 : DeclMarkerImpl(String, JSString)
214 8615092 : DeclMarkerImpl(String, JSFlatString)
215 1243 : DeclMarkerImpl(String, JSLinearString)
216 2879428 : DeclMarkerImpl(TypeObject, types::TypeObject)
217 : #if JS_HAS_XML_SUPPORT
218 235 : DeclMarkerImpl(XML, JSXML)
219 : #endif
220 :
221 : /*** Externally Typed Marking ***/
222 :
223 : void
224 18137003 : MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind)
225 : {
226 18137003 : JS_ASSERT(thingp);
227 18137003 : JS_ASSERT(*thingp);
228 18137003 : JS_ASSERT(kind == GetGCThingTraceKind(*thingp));
229 18137003 : switch (kind) {
230 : case JSTRACE_OBJECT:
231 12851560 : MarkInternal(trc, reinterpret_cast<JSObject **>(thingp));
232 12851560 : break;
233 : case JSTRACE_STRING:
234 2044711 : MarkInternal(trc, reinterpret_cast<JSString **>(thingp));
235 2044711 : break;
236 : case JSTRACE_SCRIPT:
237 2170865 : MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
238 2170865 : break;
239 : case JSTRACE_SHAPE:
240 928849 : MarkInternal(trc, reinterpret_cast<Shape **>(thingp));
241 928849 : break;
242 : case JSTRACE_BASE_SHAPE:
243 118660 : MarkInternal(trc, reinterpret_cast<BaseShape **>(thingp));
244 118660 : break;
245 : case JSTRACE_TYPE_OBJECT:
246 22304 : MarkInternal(trc, reinterpret_cast<types::TypeObject **>(thingp));
247 22304 : break;
248 : #if JS_HAS_XML_SUPPORT
249 : case JSTRACE_XML:
250 54 : MarkInternal(trc, reinterpret_cast<JSXML **>(thingp));
251 54 : break;
252 : #endif
253 : }
254 18137003 : }
255 :
256 : void
257 22828 : MarkGCThingRoot(JSTracer *trc, void **thingp, const char *name)
258 : {
259 22828 : JS_ROOT_MARKING_ASSERT(trc);
260 22828 : JS_SET_TRACING_NAME(trc, name);
261 22828 : JS_ASSERT(thingp);
262 22828 : if (!*thingp)
263 0 : return;
264 22828 : MarkKind(trc, thingp, GetGCThingTraceKind(*thingp));
265 : }
266 :
267 : /*** ID Marking ***/
268 :
269 : static inline void
270 3730945 : MarkIdInternal(JSTracer *trc, jsid *id)
271 : {
272 3730945 : if (JSID_IS_STRING(*id)) {
273 3187372 : JSString *str = JSID_TO_STRING(*id);
274 3187372 : MarkInternal(trc, &str);
275 3187372 : *id = ATOM_TO_JSID(reinterpret_cast<JSAtom *>(str));
276 543573 : } else if (JS_UNLIKELY(JSID_IS_OBJECT(*id))) {
277 0 : JSObject *obj = JSID_TO_OBJECT(*id);
278 0 : MarkInternal(trc, &obj);
279 0 : *id = OBJECT_TO_JSID(obj);
280 : }
281 3730945 : }
282 :
283 : void
284 3672790 : MarkId(JSTracer *trc, HeapId *id, const char *name)
285 : {
286 3672790 : JS_SET_TRACING_NAME(trc, name);
287 3672790 : MarkIdInternal(trc, id->unsafeGet());
288 3672790 : }
289 :
290 : void
291 0 : MarkIdRoot(JSTracer *trc, jsid *id, const char *name)
292 : {
293 0 : JS_ROOT_MARKING_ASSERT(trc);
294 0 : JS_SET_TRACING_NAME(trc, name);
295 0 : MarkIdInternal(trc, id);
296 0 : }
297 :
298 : void
299 0 : MarkIdRange(JSTracer *trc, size_t len, HeapId *vec, const char *name)
300 : {
301 0 : for (size_t i = 0; i < len; ++i) {
302 0 : JS_SET_TRACING_INDEX(trc, name, i);
303 0 : MarkIdInternal(trc, vec[i].unsafeGet());
304 : }
305 0 : }
306 :
307 : void
308 1242 : MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name)
309 : {
310 1242 : JS_ROOT_MARKING_ASSERT(trc);
311 59397 : for (size_t i = 0; i < len; ++i) {
312 58155 : JS_SET_TRACING_INDEX(trc, name, i);
313 58155 : MarkIdInternal(trc, &vec[i]);
314 : }
315 1242 : }
316 :
317 : /*** Value Marking ***/
318 :
319 : static inline void
320 13855200 : MarkValueInternal(JSTracer *trc, Value *v)
321 : {
322 13855200 : if (v->isMarkable()) {
323 6957567 : JS_ASSERT(v->toGCThing());
324 6957567 : void *thing = v->toGCThing();
325 6957567 : MarkKind(trc, &thing, v->gcKind());
326 6957567 : if (v->isString())
327 1575411 : v->setString((JSString *)thing);
328 : else
329 5382156 : v->setObjectOrNull((JSObject *)thing);
330 : }
331 13855200 : }
332 :
333 : void
334 13572 : MarkValue(JSTracer *trc, HeapValue *v, const char *name)
335 : {
336 13572 : JS_SET_TRACING_NAME(trc, name);
337 13572 : MarkValueInternal(trc, v->unsafeGet());
338 13572 : }
339 :
340 : void
341 31718 : MarkValueRoot(JSTracer *trc, Value *v, const char *name)
342 : {
343 31718 : JS_ROOT_MARKING_ASSERT(trc);
344 31718 : JS_SET_TRACING_NAME(trc, name);
345 31718 : MarkValueInternal(trc, v);
346 31718 : }
347 :
348 : void
349 829305 : MarkValueRange(JSTracer *trc, size_t len, HeapValue *vec, const char *name)
350 : {
351 2480993 : for (size_t i = 0; i < len; ++i) {
352 1651688 : JS_SET_TRACING_INDEX(trc, name, i);
353 1651688 : MarkValueInternal(trc, vec[i].unsafeGet());
354 : }
355 829305 : }
356 :
357 : void
358 634041 : MarkValueRootRange(JSTracer *trc, size_t len, Value *vec, const char *name)
359 : {
360 634041 : JS_ROOT_MARKING_ASSERT(trc);
361 3825885 : for (size_t i = 0; i < len; ++i) {
362 3191844 : JS_SET_TRACING_INDEX(trc, name, i);
363 3191844 : MarkValueInternal(trc, &vec[i]);
364 : }
365 634041 : }
366 :
367 : /*** Slot Marking ***/
368 :
369 : void
370 85506 : MarkSlot(JSTracer *trc, HeapSlot *s, const char *name)
371 : {
372 85506 : JS_SET_TRACING_NAME(trc, name);
373 85506 : MarkValueInternal(trc, s->unsafeGet());
374 85506 : }
375 :
376 : void
377 5528 : MarkArraySlots(JSTracer *trc, size_t len, HeapSlot *vec, const char *name)
378 : {
379 9694 : for (size_t i = 0; i < len; ++i) {
380 4166 : JS_SET_TRACING_INDEX(trc, name, i);
381 4166 : MarkValueInternal(trc, vec[i].unsafeGet());
382 : }
383 5528 : }
384 :
385 : void
386 2840717 : MarkObjectSlots(JSTracer *trc, JSObject *obj, uint32_t start, uint32_t nslots)
387 : {
388 2840717 : JS_ASSERT(obj->isNative());
389 11110882 : for (uint32_t i = start; i < (start + nslots); ++i) {
390 8270165 : JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
391 8270165 : MarkValueInternal(trc, obj->nativeGetSlotRef(i).unsafeGet());
392 : }
393 2840717 : }
394 :
395 : void
396 2597 : MarkCrossCompartmentObjectUnbarriered(JSTracer *trc, JSObject **obj, const char *name)
397 : {
398 2597 : if (IS_GC_MARKING_TRACER(trc) && !(*obj)->compartment()->isCollecting())
399 135 : return;
400 :
401 2462 : MarkObjectUnbarriered(trc, obj, name);
402 : }
403 :
404 : void
405 1152 : MarkCrossCompartmentScriptUnbarriered(JSTracer *trc, JSScript **script, const char *name)
406 : {
407 1152 : if (IS_GC_MARKING_TRACER(trc) && !(*script)->compartment()->isCollecting())
408 72 : return;
409 :
410 1080 : MarkScriptUnbarriered(trc, script, name);
411 : }
412 :
413 : void
414 171935 : MarkCrossCompartmentSlot(JSTracer *trc, HeapSlot *s, const char *name)
415 : {
416 171935 : if (s->isMarkable()) {
417 85951 : Cell *cell = (Cell *)s->toGCThing();
418 85951 : if (IS_GC_MARKING_TRACER(trc) && !cell->compartment()->isCollecting())
419 498 : return;
420 :
421 85453 : MarkSlot(trc, s, name);
422 : }
423 : }
424 :
425 : /*** Special Marking ***/
426 :
427 : void
428 101606 : MarkObject(JSTracer *trc, HeapPtr<GlobalObject, JSScript *> *thingp, const char *name)
429 : {
430 101606 : JS_SET_TRACING_NAME(trc, name);
431 101606 : MarkInternal(trc, thingp->unsafeGet());
432 101606 : }
433 :
434 : void
435 606541 : MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name)
436 : {
437 606541 : JS_SET_TRACING_NAME(trc, name);
438 606541 : MarkValueInternal(trc, v);
439 606541 : }
440 :
441 : /*** Push Mark Stack ***/
442 :
443 : #define JS_COMPARTMENT_ASSERT(rt, thing) \
444 : JS_ASSERT((thing)->compartment()->isCollecting())
445 :
446 : #define JS_COMPARTMENT_ASSERT_STR(rt, thing) \
447 : JS_ASSERT((thing)->compartment()->isCollecting() || \
448 : (thing)->compartment() == (rt)->atomsCompartment);
449 :
450 : static void
451 246 : PushMarkStack(GCMarker *gcmarker, JSXML *thing)
452 : {
453 246 : JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
454 :
455 246 : if (thing->markIfUnmarked(gcmarker->getMarkColor()))
456 194 : gcmarker->pushXML(thing);
457 246 : }
458 :
459 : static void
460 19392247 : PushMarkStack(GCMarker *gcmarker, JSObject *thing)
461 : {
462 19392247 : JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
463 :
464 19392247 : if (thing->markIfUnmarked(gcmarker->getMarkColor()))
465 3632213 : gcmarker->pushObject(thing);
466 19392247 : }
467 :
468 : static void
469 699225 : PushMarkStack(GCMarker *gcmarker, JSFunction *thing)
470 : {
471 699225 : JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
472 :
473 699225 : if (thing->markIfUnmarked(gcmarker->getMarkColor()))
474 65563 : gcmarker->pushObject(thing);
475 699225 : }
476 :
477 : static void
478 16516391 : PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing)
479 : {
480 16516391 : JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
481 :
482 16516391 : if (thing->markIfUnmarked(gcmarker->getMarkColor()))
483 422962 : gcmarker->pushType(thing);
484 16516391 : }
485 :
486 : static void
487 : MarkChildren(JSTracer *trc, JSScript *script);
488 :
489 : static void
490 3114002 : PushMarkStack(GCMarker *gcmarker, JSScript *thing)
491 : {
492 3114002 : JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
493 :
494 : /*
495 : * We mark scripts directly rather than pushing on the stack as they can
496 : * refer to other scripts only indirectly (like via nested functions) and
497 : * we cannot get to deep recursion.
498 : */
499 3114002 : if (thing->markIfUnmarked(gcmarker->getMarkColor()))
500 127210 : MarkChildren(gcmarker, thing);
501 3114002 : }
502 :
503 : static void
504 : ScanShape(GCMarker *gcmarker, Shape *shape);
505 :
506 : static void
507 17523634 : PushMarkStack(GCMarker *gcmarker, Shape *thing)
508 : {
509 17523634 : JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
510 :
511 : /* We mark shapes directly rather than pushing on the stack. */
512 17523634 : if (thing->markIfUnmarked(gcmarker->getMarkColor()))
513 3052445 : ScanShape(gcmarker, thing);
514 17523634 : }
515 :
516 : static inline void
517 : ScanBaseShape(GCMarker *gcmarker, BaseShape *base);
518 :
519 : static void
520 18735431 : PushMarkStack(GCMarker *gcmarker, BaseShape *thing)
521 : {
522 18735431 : JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
523 :
524 : /* We mark base shapes directly rather than pushing on the stack. */
525 18735431 : if (thing->markIfUnmarked(gcmarker->getMarkColor()))
526 6027805 : ScanBaseShape(gcmarker, thing);
527 18735431 : }
528 :
529 : static void
530 18614288 : ScanShape(GCMarker *gcmarker, Shape *shape)
531 : {
532 : restart:
533 18614288 : PushMarkStack(gcmarker, shape->base());
534 :
535 18614288 : const HeapId &id = shape->propidRef();
536 18614288 : if (JSID_IS_STRING(id))
537 15468539 : PushMarkStack(gcmarker, JSID_TO_STRING(id));
538 3145749 : else if (JS_UNLIKELY(JSID_IS_OBJECT(id)))
539 0 : PushMarkStack(gcmarker, JSID_TO_OBJECT(id));
540 :
541 18614288 : shape = shape->previous();
542 18614288 : if (shape && shape->markIfUnmarked(gcmarker->getMarkColor()))
543 15561843 : goto restart;
544 3052445 : }
545 :
546 : static inline void
547 6027805 : ScanBaseShape(GCMarker *gcmarker, BaseShape *base)
548 : {
549 6027805 : base->assertConsistency();
550 :
551 6027805 : if (base->hasGetterObject())
552 1797528 : PushMarkStack(gcmarker, base->getterObject());
553 :
554 6027805 : if (base->hasSetterObject())
555 306980 : PushMarkStack(gcmarker, base->setterObject());
556 :
557 6027805 : if (JSObject *parent = base->getObjectParent())
558 5889041 : PushMarkStack(gcmarker, parent);
559 :
560 : /*
561 : * All children of the owned base shape are consistent with its
562 : * unowned one, thus we do not need to trace through children of the
563 : * unowned base shape.
564 : */
565 6027805 : if (base->isOwned()) {
566 376548 : UnownedBaseShape *unowned = base->baseUnowned();
567 376548 : JS_ASSERT(base->compartment() == unowned->compartment());
568 376548 : unowned->markIfUnmarked(gcmarker->getMarkColor());
569 : }
570 6027805 : }
571 :
572 : static inline void
573 197206293 : ScanLinearString(GCMarker *gcmarker, JSLinearString *str)
574 : {
575 197206293 : JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, str);
576 197206293 : JS_ASSERT(str->isMarked());
577 :
578 : /*
579 : * Add extra asserts to confirm the static type to detect incorrect string
580 : * mutations.
581 : */
582 197206293 : JS_ASSERT(str->JSString::isLinear());
583 394467721 : while (str->isDependent()) {
584 470891 : str = str->asDependent().base();
585 470891 : JS_ASSERT(str->JSString::isLinear());
586 470891 : JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, str);
587 470891 : if (!str->markIfUnmarked())
588 415756 : break;
589 : }
590 197206293 : }
591 :
592 : /*
593 : * The function tries to scan the whole rope tree using the marking stack as
594 : * temporary storage. If that becomes full, the unscanned ropes are added to
595 : * the delayed marking list. When the function returns, the marking stack is
596 : * at the same depth as it was on entry. This way we avoid using tags when
597 : * pushing ropes to the stack as ropes never leaks to other users of the
598 : * stack. This also assumes that a rope can only point to other ropes or
599 : * linear strings, it cannot refer to GC things of other types.
600 : */
601 : static void
602 1928175 : ScanRope(GCMarker *gcmarker, JSRope *rope)
603 : {
604 1928175 : ptrdiff_t savedPos = gcmarker->stack.position();
605 8054957 : for (;;) {
606 9983132 : JS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
607 9983132 : JS_ASSERT(rope->JSString::isRope());
608 9983132 : JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, rope);
609 9983132 : JS_ASSERT(rope->isMarked());
610 9983132 : JSRope *next = NULL;
611 :
612 9983132 : JSString *right = rope->rightChild();
613 9983132 : if (right->markIfUnmarked()) {
614 3027045 : if (right->isLinear())
615 410 : ScanLinearString(gcmarker, &right->asLinear());
616 : else
617 3026635 : next = &right->asRope();
618 : }
619 :
620 9983132 : JSString *left = rope->leftChild();
621 9983132 : if (left->markIfUnmarked()) {
622 5028515 : if (left->isLinear()) {
623 193 : ScanLinearString(gcmarker, &left->asLinear());
624 : } else {
625 : /*
626 : * When both children are ropes, set aside the right one to
627 : * scan it later.
628 : */
629 5028322 : if (next && !gcmarker->stack.push(reinterpret_cast<uintptr_t>(next)))
630 0 : gcmarker->delayMarkingChildren(next);
631 5028322 : next = &left->asRope();
632 : }
633 : }
634 9983132 : if (next) {
635 8054957 : rope = next;
636 1928175 : } else if (savedPos != gcmarker->stack.position()) {
637 0 : JS_ASSERT(savedPos < gcmarker->stack.position());
638 0 : rope = reinterpret_cast<JSRope *>(gcmarker->stack.pop());
639 : } else {
640 : break;
641 : }
642 : }
643 1928175 : JS_ASSERT(savedPos == gcmarker->stack.position());
644 1928175 : }
645 :
646 : static inline void
647 199133865 : ScanString(GCMarker *gcmarker, JSString *str)
648 : {
649 199133865 : if (str->isLinear())
650 197205690 : ScanLinearString(gcmarker, &str->asLinear());
651 : else
652 1928175 : ScanRope(gcmarker, &str->asRope());
653 199133865 : }
654 :
655 : static inline void
656 221311991 : PushMarkStack(GCMarker *gcmarker, JSString *str)
657 : {
658 221311991 : JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, str);
659 :
660 : /*
661 : * As string can only refer to other strings we fully scan its GC graph
662 : * using the explicit stack when navigating the rope tree to avoid
663 : * dealing with strings on the stack in drainMarkStack.
664 : */
665 221311991 : if (str->markIfUnmarked())
666 194150513 : ScanString(gcmarker, str);
667 221311991 : }
668 :
669 : void
670 2872168 : MarkChildren(JSTracer *trc, JSObject *obj)
671 : {
672 2872168 : obj->markChildren(trc);
673 2872168 : }
674 :
675 : static void
676 17387228 : MarkChildren(JSTracer *trc, JSString *str)
677 : {
678 17387228 : if (str->isDependent())
679 1000 : str->asDependent().markChildren(trc);
680 17386228 : else if (str->isRope())
681 2172 : str->asRope().markChildren(trc);
682 17387228 : }
683 :
684 : static void
685 152539 : MarkChildren(JSTracer *trc, JSScript *script)
686 : {
687 152539 : script->markChildren(trc);
688 152539 : }
689 :
690 : static void
691 3670845 : MarkChildren(JSTracer *trc, Shape *shape)
692 : {
693 3670845 : shape->markChildren(trc);
694 3670845 : }
695 :
696 : static void
697 1126427 : MarkChildren(JSTracer *trc, BaseShape *base)
698 : {
699 1126427 : base->markChildren(trc);
700 1126427 : }
701 :
702 : /*
703 : * This function is used by the cycle collector to trace through the
704 : * children of a BaseShape (and its baseUnowned(), if any). The cycle
705 : * collector does not directly care about BaseShapes, so only the
706 : * getter, setter, and parent are marked. Furthermore, the parent is
707 : * marked only if it isn't the same as prevParent, which will be
708 : * updated to the current shape's parent.
709 : */
710 : inline void
711 0 : MarkCycleCollectorChildren(JSTracer *trc, BaseShape *base, JSObject **prevParent)
712 : {
713 0 : JS_ASSERT(base);
714 :
715 : /*
716 : * The cycle collector does not need to trace unowned base shapes,
717 : * as they have the same getter, setter and parent as the original
718 : * base shape.
719 : */
720 0 : base->assertConsistency();
721 :
722 0 : if (base->hasGetterObject()) {
723 0 : JSObject *tmp = base->getterObject();
724 0 : MarkObjectUnbarriered(trc, &tmp, "getter");
725 0 : JS_ASSERT(tmp == base->getterObject());
726 : }
727 :
728 0 : if (base->hasSetterObject()) {
729 0 : JSObject *tmp = base->setterObject();
730 0 : MarkObjectUnbarriered(trc, &tmp, "setter");
731 0 : JS_ASSERT(tmp == base->setterObject());
732 : }
733 :
734 0 : JSObject *parent = base->getObjectParent();
735 0 : if (parent && parent != *prevParent) {
736 0 : MarkObjectUnbarriered(trc, &parent, "parent");
737 0 : JS_ASSERT(parent == base->getObjectParent());
738 0 : *prevParent = parent;
739 : }
740 0 : }
741 :
742 : /*
743 : * This function is used by the cycle collector to trace through a
744 : * shape. The cycle collector does not care about shapes or base
745 : * shapes, so those are not marked. Instead, any shapes or base shapes
746 : * that are encountered have their children marked. Stack space is
747 : * bounded. If two shapes in a row have the same parent pointer, the
748 : * parent pointer will only be marked once.
749 : */
750 : void
751 0 : MarkCycleCollectorChildren(JSTracer *trc, Shape *shape)
752 : {
753 0 : JSObject *prevParent = NULL;
754 0 : do {
755 0 : MarkCycleCollectorChildren(trc, shape->base(), &prevParent);
756 0 : MarkId(trc, &shape->propidRef(), "propid");
757 0 : shape = shape->previous();
758 : } while (shape);
759 0 : }
760 :
761 : static void
762 422238 : ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
763 : {
764 422238 : if (!type->singleton) {
765 312574 : unsigned count = type->getPropertyCount();
766 324241 : for (unsigned i = 0; i < count; i++) {
767 11667 : types::Property *prop = type->getProperty(i);
768 11667 : if (prop && JSID_IS_STRING(prop->id))
769 8974 : PushMarkStack(gcmarker, JSID_TO_STRING(prop->id));
770 : }
771 : }
772 :
773 422238 : if (type->proto)
774 377354 : PushMarkStack(gcmarker, type->proto);
775 :
776 422238 : if (type->singleton && !type->lazy())
777 64814 : PushMarkStack(gcmarker, type->singleton);
778 :
779 422238 : if (type->newScript) {
780 5 : PushMarkStack(gcmarker, type->newScript->fun);
781 5 : PushMarkStack(gcmarker, type->newScript->shape);
782 : }
783 :
784 422238 : if (type->interpretedFunction)
785 37268 : PushMarkStack(gcmarker, type->interpretedFunction);
786 422238 : }
787 :
788 : static void
789 93503 : MarkChildren(JSTracer *trc, types::TypeObject *type)
790 : {
791 93503 : if (!type->singleton) {
792 83015 : unsigned count = type->getPropertyCount();
793 84942 : for (unsigned i = 0; i < count; i++) {
794 1927 : types::Property *prop = type->getProperty(i);
795 1927 : if (prop)
796 1927 : MarkId(trc, &prop->id, "type_prop");
797 : }
798 : }
799 :
800 93503 : if (type->proto)
801 83303 : MarkObject(trc, &type->proto, "type_proto");
802 :
803 93503 : if (type->singleton && !type->lazy())
804 6744 : MarkObject(trc, &type->singleton, "type_singleton");
805 :
806 93503 : if (type->newScript) {
807 100 : MarkObject(trc, &type->newScript->fun, "type_new_function");
808 100 : MarkShape(trc, &type->newScript->shape, "type_new_shape");
809 : }
810 :
811 93503 : if (type->interpretedFunction)
812 9998 : MarkObject(trc, &type->interpretedFunction, "type_function");
813 93503 : }
814 :
815 : #ifdef JS_HAS_XML_SUPPORT
816 : static void
817 194 : MarkChildren(JSTracer *trc, JSXML *xml)
818 : {
819 194 : js_TraceXML(trc, xml);
820 194 : }
821 : #endif
822 :
823 : template<typename T>
824 : void
825 9 : PushArenaTyped(GCMarker *gcmarker, ArenaHeader *aheader)
826 : {
827 18 : for (CellIterUnderGC i(aheader); !i.done(); i.next())
828 9 : PushMarkStack(gcmarker, i.get<T>());
829 9 : }
830 :
831 : void
832 9 : PushArena(GCMarker *gcmarker, ArenaHeader *aheader)
833 : {
834 9 : switch (MapAllocToTraceKind(aheader->getAllocKind())) {
835 : case JSTRACE_OBJECT:
836 0 : PushArenaTyped<JSObject>(gcmarker, aheader);
837 0 : break;
838 :
839 : case JSTRACE_STRING:
840 9 : PushArenaTyped<JSString>(gcmarker, aheader);
841 9 : break;
842 :
843 : case JSTRACE_SCRIPT:
844 0 : PushArenaTyped<JSScript>(gcmarker, aheader);
845 0 : break;
846 :
847 : case JSTRACE_SHAPE:
848 0 : PushArenaTyped<js::Shape>(gcmarker, aheader);
849 0 : break;
850 :
851 : case JSTRACE_BASE_SHAPE:
852 0 : PushArenaTyped<js::BaseShape>(gcmarker, aheader);
853 0 : break;
854 :
855 : case JSTRACE_TYPE_OBJECT:
856 0 : PushArenaTyped<js::types::TypeObject>(gcmarker, aheader);
857 0 : break;
858 :
859 : #if JS_HAS_XML_SUPPORT
860 : case JSTRACE_XML:
861 0 : PushArenaTyped<JSXML>(gcmarker, aheader);
862 0 : break;
863 : #endif
864 : }
865 9 : }
866 :
867 : } /* namespace gc */
868 :
869 : using namespace js::gc;
870 :
871 : struct SlotArrayLayout
872 : {
873 : union {
874 : HeapSlot *end;
875 : js::Class *clasp;
876 : };
877 : union {
878 : HeapSlot *start;
879 : uintptr_t index;
880 : };
881 : JSObject *obj;
882 :
883 : static void staticAsserts() {
884 : /* This should have the same layout as three mark stack items. */
885 : JS_STATIC_ASSERT(sizeof(SlotArrayLayout) == 3 * sizeof(uintptr_t));
886 : }
887 : };
888 :
889 : /*
890 : * During incremental GC, we return from drainMarkStack without having processed
891 : * the entire stack. At that point, JS code can run and reallocate slot arrays
892 : * that are stored on the stack. To prevent this from happening, we replace all
893 : * ValueArrayTag stack items with SavedValueArrayTag. In the latter, slots
894 : * pointers are replaced with slot indexes.
895 : *
896 : * We also replace the slot array end pointer (which can be derived from the obj
897 : * pointer) with the object's class. During JS executation, array slowification
898 : * can cause the layout of slots to change. We can observe that slowification
899 : * happened if the class changed; in that case, we completely rescan the array.
900 : */
901 : void
902 54 : GCMarker::saveValueRanges()
903 : {
904 381 : for (uintptr_t *p = stack.tos; p > stack.stack; ) {
905 273 : uintptr_t tag = *--p & StackTagMask;
906 273 : if (tag == ValueArrayTag) {
907 36 : p -= 2;
908 36 : SlotArrayLayout *arr = reinterpret_cast<SlotArrayLayout *>(p);
909 36 : JSObject *obj = arr->obj;
910 :
911 36 : if (obj->getClass() == &ArrayClass) {
912 0 : HeapSlot *vp = obj->getDenseArrayElements();
913 0 : JS_ASSERT(arr->start >= vp &&
914 0 : arr->end == vp + obj->getDenseArrayInitializedLength());
915 0 : arr->index = arr->start - vp;
916 : } else {
917 36 : HeapSlot *vp = obj->fixedSlots();
918 36 : unsigned nfixed = obj->numFixedSlots();
919 36 : if (arr->start >= vp && arr->start < vp + nfixed) {
920 18 : JS_ASSERT(arr->end == vp + Min(nfixed, obj->slotSpan()));
921 18 : arr->index = arr->start - vp;
922 : } else {
923 18 : JS_ASSERT(arr->start >= obj->slots &&
924 36 : arr->end == obj->slots + obj->slotSpan() - nfixed);
925 18 : arr->index = (arr->start - obj->slots) + nfixed;
926 : }
927 : }
928 36 : arr->clasp = obj->getClass();
929 36 : p[2] |= SavedValueArrayTag;
930 237 : } else if (tag == SavedValueArrayTag) {
931 0 : p -= 2;
932 : }
933 : }
934 54 : }
935 :
936 : bool
937 0 : GCMarker::restoreValueArray(JSObject *obj, void **vpp, void **endp)
938 : {
939 0 : uintptr_t start = stack.pop();
940 0 : js::Class *clasp = reinterpret_cast<js::Class *>(stack.pop());
941 :
942 0 : JS_ASSERT(obj->getClass() == clasp ||
943 0 : (clasp == &ArrayClass && obj->getClass() == &SlowArrayClass));
944 :
945 0 : if (clasp == &ArrayClass) {
946 0 : if (obj->getClass() != &ArrayClass)
947 0 : return false;
948 :
949 0 : uint32_t initlen = obj->getDenseArrayInitializedLength();
950 0 : HeapSlot *vp = obj->getDenseArrayElements();
951 0 : if (start < initlen) {
952 0 : *vpp = vp + start;
953 0 : *endp = vp + initlen;
954 : } else {
955 : /* The object shrunk, in which case no scanning is needed. */
956 0 : *vpp = *endp = vp;
957 : }
958 : } else {
959 0 : HeapSlot *vp = obj->fixedSlots();
960 0 : unsigned nfixed = obj->numFixedSlots();
961 0 : unsigned nslots = obj->slotSpan();
962 0 : if (start < nfixed) {
963 0 : *vpp = vp + start;
964 0 : *endp = vp + Min(nfixed, nslots);
965 0 : } else if (start < nslots) {
966 0 : *vpp = obj->slots + start - nfixed;
967 0 : *endp = obj->slots + nslots - nfixed;
968 : } else {
969 : /* The object shrunk, in which case no scanning is needed. */
970 0 : *vpp = *endp = obj->slots;
971 : }
972 : }
973 :
974 0 : JS_ASSERT(*vpp <= *endp);
975 0 : return true;
976 : }
977 :
978 : inline void
979 15589580 : GCMarker::processMarkStackTop(SliceBudget &budget)
980 : {
981 : /*
982 : * The function uses explicit goto and implements the scanning of the
983 : * object directly. It allows to eliminate the tail recursion and
984 : * significantly improve the marking performance, see bug 641025.
985 : */
986 : HeapSlot *vp, *end;
987 : JSObject *obj;
988 :
989 15589580 : uintptr_t addr = stack.pop();
990 15589580 : uintptr_t tag = addr & StackTagMask;
991 15589580 : addr &= ~StackTagMask;
992 :
993 15589580 : if (tag == ValueArrayTag) {
994 : JS_STATIC_ASSERT(ValueArrayTag == 0);
995 11472011 : JS_ASSERT(!(addr & Cell::CellMask));
996 11472011 : obj = reinterpret_cast<JSObject *>(addr);
997 11472011 : uintptr_t addr2 = stack.pop();
998 11472011 : uintptr_t addr3 = stack.pop();
999 11472011 : JS_ASSERT(addr2 <= addr3);
1000 11472011 : JS_ASSERT((addr3 - addr2) % sizeof(Value) == 0);
1001 11472011 : vp = reinterpret_cast<HeapSlot *>(addr2);
1002 11472011 : end = reinterpret_cast<HeapSlot *>(addr3);
1003 11472011 : goto scan_value_array;
1004 : }
1005 :
1006 4117569 : if (tag == ObjectTag) {
1007 3695137 : obj = reinterpret_cast<JSObject *>(addr);
1008 3695137 : JS_COMPARTMENT_ASSERT(runtime, obj);
1009 3695137 : goto scan_obj;
1010 : }
1011 :
1012 422432 : if (tag == TypeTag) {
1013 422238 : ScanTypeObject(this, reinterpret_cast<types::TypeObject *>(addr));
1014 194 : } else if (tag == SavedValueArrayTag) {
1015 0 : JS_ASSERT(!(addr & Cell::CellMask));
1016 0 : obj = reinterpret_cast<JSObject *>(addr);
1017 0 : if (restoreValueArray(obj, (void **)&vp, (void **)&end))
1018 0 : goto scan_value_array;
1019 : else
1020 0 : goto scan_obj;
1021 : } else {
1022 194 : JS_ASSERT(tag == XmlTag);
1023 194 : MarkChildren(this, reinterpret_cast<JSXML *>(addr));
1024 : }
1025 422432 : budget.step();
1026 422432 : return;
1027 :
1028 : scan_value_array:
1029 27947880 : JS_ASSERT(vp <= end);
1030 95680042 : while (vp != end) {
1031 52576017 : budget.step();
1032 52576017 : if (budget.isOverBudget()) {
1033 18 : pushValueArray(obj, vp, end);
1034 18 : return;
1035 : }
1036 :
1037 52575999 : const Value &v = *vp++;
1038 52575999 : if (v.isString()) {
1039 12782586 : JSString *str = v.toString();
1040 12782586 : JS_COMPARTMENT_ASSERT_STR(runtime, str);
1041 12782586 : if (str->markIfUnmarked())
1042 4983352 : ScanString(this, str);
1043 39793413 : } else if (v.isObject()) {
1044 19760943 : JSObject *obj2 = &v.toObject();
1045 19760943 : JS_COMPARTMENT_ASSERT(runtime, obj2);
1046 19760943 : if (obj2->markIfUnmarked(getMarkColor())) {
1047 12791717 : pushValueArray(obj, vp, end);
1048 12791717 : obj = obj2;
1049 12791717 : goto scan_obj;
1050 : }
1051 : }
1052 : }
1053 15156145 : return;
1054 :
1055 : scan_obj:
1056 : {
1057 16486854 : JS_COMPARTMENT_ASSERT(runtime, obj);
1058 :
1059 16486854 : budget.step();
1060 16486854 : if (budget.isOverBudget()) {
1061 27 : pushObject(obj);
1062 27 : return;
1063 : }
1064 :
1065 16486827 : types::TypeObject *type = obj->typeFromGC();
1066 16486827 : PushMarkStack(this, type);
1067 :
1068 16486827 : Shape *shape = obj->lastProperty();
1069 16486827 : PushMarkStack(this, shape);
1070 :
1071 : /* Call the trace hook if necessary. */
1072 16486827 : Class *clasp = shape->getObjectClass();
1073 16486827 : if (clasp->trace) {
1074 14239843 : if (clasp == &ArrayClass) {
1075 625429 : JS_ASSERT(!shape->isNative());
1076 625429 : vp = obj->getDenseArrayElements();
1077 625429 : end = vp + obj->getDenseArrayInitializedLength();
1078 625429 : goto scan_value_array;
1079 : } else {
1080 0 : JS_ASSERT_IF(runtime->gcIncrementalState != NO_INCREMENTAL,
1081 13614414 : clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
1082 : }
1083 13614414 : clasp->trace(this, obj);
1084 : }
1085 :
1086 15861398 : if (!shape->isNative())
1087 10958 : return;
1088 :
1089 15850440 : unsigned nslots = obj->slotSpan();
1090 15850440 : vp = obj->fixedSlots();
1091 15850440 : if (obj->slots) {
1092 5812532 : unsigned nfixed = obj->numFixedSlots();
1093 5812532 : if (nslots > nfixed) {
1094 5812532 : pushValueArray(obj, vp, vp + nfixed);
1095 5812532 : vp = obj->slots;
1096 5812532 : end = vp + (nslots - nfixed);
1097 5812532 : goto scan_value_array;
1098 : }
1099 : }
1100 10037908 : JS_ASSERT(nslots <= obj->numFixedSlots());
1101 10037908 : end = vp + nslots;
1102 10037908 : goto scan_value_array;
1103 : }
1104 : }
1105 :
1106 : bool
1107 77246 : GCMarker::drainMarkStack(SliceBudget &budget)
1108 : {
1109 : #ifdef DEBUG
1110 77246 : JSRuntime *rt = runtime;
1111 :
1112 : struct AutoCheckCompartment {
1113 : JSRuntime *runtime;
1114 77246 : AutoCheckCompartment(JSRuntime *rt) : runtime(rt) {
1115 77246 : JS_ASSERT(!rt->gcStrictCompartmentChecking);
1116 77246 : runtime->gcStrictCompartmentChecking = true;
1117 77246 : }
1118 77246 : ~AutoCheckCompartment() { runtime->gcStrictCompartmentChecking = false; }
1119 154492 : } acc(rt);
1120 : #endif
1121 :
1122 77246 : if (budget.isOverBudget())
1123 0 : return false;
1124 :
1125 9 : for (;;) {
1126 15744036 : while (!stack.isEmpty()) {
1127 15589580 : processMarkStackTop(budget);
1128 15589580 : if (budget.isOverBudget()) {
1129 54 : saveValueRanges();
1130 54 : return false;
1131 : }
1132 : }
1133 :
1134 77201 : if (!hasDelayedChildren())
1135 : break;
1136 :
1137 : /*
1138 : * Mark children of things that caused too deep recursion during the
1139 : * above tracing. Don't do this until we're done with everything
1140 : * else.
1141 : */
1142 9 : if (!markDelayedChildren(budget)) {
1143 0 : saveValueRanges();
1144 0 : return false;
1145 : }
1146 : }
1147 :
1148 77192 : return true;
1149 : }
1150 :
1151 : void
1152 25175500 : TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
1153 : {
1154 25175500 : switch (kind) {
1155 : case JSTRACE_OBJECT:
1156 2872168 : MarkChildren(trc, static_cast<JSObject *>(thing));
1157 2872168 : break;
1158 :
1159 : case JSTRACE_STRING:
1160 17387228 : MarkChildren(trc, static_cast<JSString *>(thing));
1161 17387228 : break;
1162 :
1163 : case JSTRACE_SCRIPT:
1164 25329 : MarkChildren(trc, static_cast<JSScript *>(thing));
1165 25329 : break;
1166 :
1167 : case JSTRACE_SHAPE:
1168 3670845 : MarkChildren(trc, static_cast<Shape *>(thing));
1169 3670845 : break;
1170 :
1171 : case JSTRACE_BASE_SHAPE:
1172 1126427 : MarkChildren(trc, static_cast<BaseShape *>(thing));
1173 1126427 : break;
1174 :
1175 : case JSTRACE_TYPE_OBJECT:
1176 93503 : MarkChildren(trc, (types::TypeObject *)thing);
1177 93503 : break;
1178 :
1179 : #if JS_HAS_XML_SUPPORT
1180 : case JSTRACE_XML:
1181 0 : MarkChildren(trc, static_cast<JSXML *>(thing));
1182 0 : break;
1183 : #endif
1184 : }
1185 25175500 : }
1186 :
1187 : void
1188 0 : CallTracer(JSTracer *trc, void *thing, JSGCTraceKind kind)
1189 : {
1190 0 : JS_ASSERT(thing);
1191 0 : void *tmp = thing;
1192 0 : MarkKind(trc, &tmp, kind);
1193 0 : JS_ASSERT(tmp == thing);
1194 0 : }
1195 :
1196 : } /* namespace js */
|