1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is SpiderMonkey code.
18 * The Initial Developer of the Original Code is
19 * Mozilla Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 2010
21 * the Initial Developer. All Rights Reserved.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #ifndef jsgcinlines_h___
41 #define jsgcinlines_h___
45 #include "jscompartment.h"
51 # define METER(x) ((void) (x))
52 # define METER_IF(condition, x) ((void) ((condition) && (x)))
54 # define METER(x) ((void) 0)
55 # define METER_IF(condition, x) ((void) 0)
61 /* Capacity for slotsToThingKind */
62 const size_t SLOTS_TO_THING_KIND_LIMIT
= 17;
64 /* Get the best kind to use when making an object with the given slot count. */
65 static inline FinalizeKind
66 GetGCObjectKind(size_t numSlots
)
68 extern FinalizeKind slotsToThingKind
[];
70 if (numSlots
>= SLOTS_TO_THING_KIND_LIMIT
)
71 return FINALIZE_OBJECT0
;
72 return slotsToThingKind
[numSlots
];
75 /* Get the number of fixed slots and initial capacity associated with a kind. */
77 GetGCKindSlots(FinalizeKind thingKind
)
79 /* Using a switch in hopes that thingKind will usually be a compile-time constant. */
81 case FINALIZE_OBJECT0
:
83 case FINALIZE_OBJECT2
:
85 case FINALIZE_OBJECT4
:
87 case FINALIZE_OBJECT8
:
89 case FINALIZE_OBJECT12
:
91 case FINALIZE_OBJECT16
:
94 JS_NOT_REACHED("Bad object finalize kind");
103 * Allocates a new GC thing. After a successful allocation the caller must
104 * fully initialize the thing before calling any function that can potentially
105 * trigger GC. This will ensure that GC tracing never sees junk values stored
106 * in the partially initialized thing.
109 template <typename T
>
111 NewFinalizableGCThing(JSContext
*cx
, unsigned thingKind
)
113 JS_ASSERT(thingKind
< js::gc::FINALIZE_LIMIT
);
115 JS_ASSERT_IF((cx
->compartment
== cx
->runtime
->atomsCompartment
),
116 (thingKind
== js::gc::FINALIZE_STRING
) ||
117 (thingKind
== js::gc::FINALIZE_SHORT_STRING
));
120 METER(cx
->compartment
->compartmentStats
[thingKind
].alloc
++);
122 js::gc::FreeCell
*cell
= cx
->compartment
->freeLists
.getNext(thingKind
);
124 CheckGCFreeListLink(cell
);
127 if (!RefillFinalizableFreeList(cx
, thingKind
))
136 js_NewGCObject(JSContext
*cx
, js::gc::FinalizeKind kind
)
138 JS_ASSERT(kind
>= js::gc::FINALIZE_OBJECT0
&& kind
<= js::gc::FINALIZE_OBJECT_LAST
);
139 JSObject
*obj
= NewFinalizableGCThing
<JSObject
>(cx
, kind
);
141 obj
->capacity
= js::gc::GetGCKindSlots(kind
);
146 js_NewGCString(JSContext
*cx
)
148 return NewFinalizableGCThing
<JSString
>(cx
, js::gc::FINALIZE_STRING
);
151 inline JSShortString
*
152 js_NewGCShortString(JSContext
*cx
)
154 return NewFinalizableGCThing
<JSShortString
>(cx
, js::gc::FINALIZE_SHORT_STRING
);
157 inline JSExternalString
*
158 js_NewGCExternalString(JSContext
*cx
, uintN type
)
160 JS_ASSERT(type
< JSExternalString::TYPE_LIMIT
);
161 JSExternalString
*str
= NewFinalizableGCThing
<JSExternalString
>(cx
, js::gc::FINALIZE_EXTERNAL_STRING
);
166 js_NewGCFunction(JSContext
*cx
)
168 JSFunction
*fun
= NewFinalizableGCThing
<JSFunction
>(cx
, js::gc::FINALIZE_FUNCTION
);
170 fun
->capacity
= JSObject::FUN_CLASS_RESERVED_SLOTS
;
174 #if JS_HAS_XML_SUPPORT
176 js_NewGCXML(JSContext
*cx
)
178 return NewFinalizableGCThing
<JSXML
>(cx
, js::gc::FINALIZE_XML
);
185 static JS_ALWAYS_INLINE
void
186 TypedMarker(JSTracer
*trc
, JSXML
*thing
);
188 static JS_ALWAYS_INLINE
void
189 TypedMarker(JSTracer
*trc
, JSObject
*thing
);
191 static JS_ALWAYS_INLINE
void
192 TypedMarker(JSTracer
*trc
, JSFunction
*thing
);
194 static JS_ALWAYS_INLINE
void
195 TypedMarker(JSTracer
*trc
, JSShortString
*thing
);
197 static JS_ALWAYS_INLINE
void
198 TypedMarker(JSTracer
*trc
, JSString
*thing
);
201 static JS_ALWAYS_INLINE
void
202 Mark(JSTracer
*trc
, T
*thing
)
205 JS_ASSERT(JS_IS_VALID_TRACE_KIND(GetGCThingTraceKind(thing
)));
206 JS_ASSERT(trc
->debugPrinter
|| trc
->debugPrintArg
);
208 /* Per-Compartment GC only with GCMarker and no custom JSTracer */
209 JS_ASSERT_IF(trc
->context
->runtime
->gcCurrentCompartment
, IS_GC_MARKING_TRACER(trc
));
211 JSRuntime
*rt
= trc
->context
->runtime
;
212 /* Don't mark things outside a compartment if we are in a per-compartment GC */
213 if (rt
->gcCurrentCompartment
&& thing
->asCell()->compartment() != rt
->gcCurrentCompartment
)
216 if (!IS_GC_MARKING_TRACER(trc
)) {
217 uint32 kind
= GetGCThingTraceKind(thing
);
218 trc
->callback(trc
, thing
, kind
);
222 TypedMarker(trc
, thing
);
226 trc
->debugPrinter
= NULL
;
227 trc
->debugPrintArg
= NULL
;
229 return; /* to avoid out: right_curl when DEBUG is not defined */
233 MarkString(JSTracer
*trc
, JSString
*str
)
236 if (JSString::isStatic(str
))
238 JS_ASSERT(GetArena
<JSString
>((Cell
*)str
)->assureThingIsAligned((JSString
*)str
));
243 MarkString(JSTracer
*trc
, JSString
*str
, const char *name
)
246 JS_SET_TRACING_NAME(trc
, name
);
247 MarkString(trc
, str
);
251 MarkObject(JSTracer
*trc
, JSObject
&obj
, const char *name
)
255 JS_SET_TRACING_NAME(trc
, name
);
256 JS_ASSERT(GetArena
<JSObject
>((Cell
*)&obj
)->assureThingIsAligned(&obj
) ||
257 GetArena
<JSObject_Slots2
>((Cell
*)&obj
)->assureThingIsAligned(&obj
) ||
258 GetArena
<JSObject_Slots4
>((Cell
*)&obj
)->assureThingIsAligned(&obj
) ||
259 GetArena
<JSObject_Slots8
>((Cell
*)&obj
)->assureThingIsAligned(&obj
) ||
260 GetArena
<JSObject_Slots12
>((Cell
*)&obj
)->assureThingIsAligned(&obj
) ||
261 GetArena
<JSObject_Slots16
>((Cell
*)&obj
)->assureThingIsAligned(&obj
) ||
262 GetArena
<JSFunction
>((Cell
*)&obj
)->assureThingIsAligned(&obj
));
267 MarkChildren(JSTracer
*trc
, JSObject
*obj
)
269 /* If obj has no map, it must be a newborn. */
273 /* Trace universal (ops-independent) members. */
274 if (JSObject
*proto
= obj
->getProto())
275 MarkObject(trc
, *proto
, "proto");
276 if (JSObject
*parent
= obj
->getParent())
277 MarkObject(trc
, *parent
, "parent");
279 if (obj
->emptyShapes
) {
280 int count
= FINALIZE_OBJECT_LAST
- FINALIZE_OBJECT0
+ 1;
281 for (int i
= 0; i
< count
; i
++) {
282 if (obj
->emptyShapes
[i
])
283 obj
->emptyShapes
[i
]->trace(trc
);
287 /* Delegate to ops or the native marking op. */
288 TraceOp op
= obj
->getOps()->trace
;
289 (op
? op
: js_TraceObject
)(trc
, obj
);
293 MarkChildren(JSTracer
*trc
, JSString
*str
)
295 if (str
->isDependent())
296 MarkString(trc
, str
->dependentBase(), "base");
297 else if (str
->isRope()) {
298 MarkString(trc
, str
->ropeLeft(), "left child");
299 MarkString(trc
, str
->ropeRight(), "right child");
303 #ifdef JS_HAS_XML_SUPPORT
305 MarkChildren(JSTracer
*trc
, JSXML
*xml
)
307 js_TraceXML(trc
, xml
);
312 RecursionTooDeep(GCMarker
*gcmarker
) {
313 #ifdef JS_GC_ASSUME_LOW_C_STACK
317 return !JS_CHECK_STACK_SIZE(gcmarker
->stackLimit
, &stackDummy
);
321 static JS_ALWAYS_INLINE
void
322 TypedMarker(JSTracer
*trc
, JSXML
*thing
)
324 if (!reinterpret_cast<Cell
*>(thing
)->markIfUnmarked(reinterpret_cast<GCMarker
*>(trc
)->getMarkColor()))
326 GCMarker
*gcmarker
= static_cast<GCMarker
*>(trc
);
327 if (RecursionTooDeep(gcmarker
)) {
328 gcmarker
->delayMarkingChildren(thing
);
330 MarkChildren(trc
, thing
);
334 static JS_ALWAYS_INLINE
void
335 TypedMarker(JSTracer
*trc
, JSObject
*thing
)
338 JS_ASSERT(JSTRACE_OBJECT
== GetFinalizableTraceKind(thing
->asCell()->arena()->header()->thingKind
));
340 GCMarker
*gcmarker
= static_cast<GCMarker
*>(trc
);
341 if (!thing
->markIfUnmarked(gcmarker
->getMarkColor()))
344 if (RecursionTooDeep(gcmarker
)) {
345 gcmarker
->delayMarkingChildren(thing
);
347 MarkChildren(trc
, thing
);
351 static JS_ALWAYS_INLINE
void
352 TypedMarker(JSTracer
*trc
, JSFunction
*thing
)
355 JS_ASSERT(JSTRACE_OBJECT
== GetFinalizableTraceKind(thing
->asCell()->arena()->header()->thingKind
));
357 GCMarker
*gcmarker
= static_cast<GCMarker
*>(trc
);
358 if (!thing
->markIfUnmarked(gcmarker
->getMarkColor()))
361 if (RecursionTooDeep(gcmarker
)) {
362 gcmarker
->delayMarkingChildren(thing
);
364 MarkChildren(trc
, static_cast<JSObject
*>(thing
));
368 static JS_ALWAYS_INLINE
void
369 TypedMarker(JSTracer
*trc
, JSShortString
*thing
)
372 * A short string cannot refer to other GC things so we don't have
373 * anything to mark if the string was unmarked and ignore the
374 * markIfUnmarked result.
376 (void) thing
->asCell()->markIfUnmarked();
383 static JS_ALWAYS_INLINE JSString
*
386 JS_ASSERT(!(size_t(str
) & 1));
387 return (JSString
*)(size_t(str
) | 1);
390 static JS_ALWAYS_INLINE
bool
391 Tagged(JSString
*str
)
393 return (size_t(str
) & 1) != 0;
396 static JS_ALWAYS_INLINE JSString
*
399 JS_ASSERT((size_t(str
) & 1) == 1);
400 return (JSString
*)(size_t(str
) & ~size_t(1));
403 static JS_ALWAYS_INLINE
void
404 NonRopeTypedMarker(JSRuntime
*rt
, JSString
*str
)
406 /* N.B. The base of a dependent string is not necessarily flat. */
407 JS_ASSERT(!str
->isRope());
409 if (rt
->gcCurrentCompartment
) {
411 if (JSString::isStatic(str
))
415 * If we perform single-compartment GC don't mark Strings outside the current compartment.
416 * Dependent Strings are not shared between compartments and they can't be in the atomsCompartment.
418 if (str
->asCell()->compartment() != rt
->gcCurrentCompartment
) {
419 JS_ASSERT(str
->asCell()->compartment() == rt
->atomsCompartment
);
422 if (!str
->asCell()->markIfUnmarked())
424 if (!str
->isDependent())
426 str
= str
->dependentBase();
429 while (!JSString::isStatic(str
) &&
430 str
->asCell()->markIfUnmarked() &&
431 str
->isDependent()) {
432 str
= str
->dependentBase();
437 } /* namespace detail */
441 static JS_ALWAYS_INLINE
void
442 TypedMarker(JSTracer
*trc
, JSString
*str
)
444 using namespace detail
;
445 JSRuntime
*rt
= trc
->context
->runtime
;
446 JS_ASSERT(!JSString::isStatic(str
));
448 JSCompartment
*strComp
= str
->asCell()->compartment();
450 if (!str
->isRope()) {
451 NonRopeTypedMarker(rt
, str
);
456 * This function must not fail, so a simple stack-based traversal must not
457 * be used (since it may oom if the stack grows large). Instead, strings
458 * are temporarily mutated to embed parent pointers as they are traversed.
459 * This algorithm is homomorphic to JSString::flatten.
461 JSString
*parent
= NULL
;
463 JS_ASSERT(strComp
== str
->asCell()->compartment() || str
->asCell()->compartment() == rt
->atomsCompartment
);
464 JS_ASSERT(!JSString::isStatic(str
));
465 if (!str
->asCell()->markIfUnmarked())
467 JSString
*left
= str
->ropeLeft();
468 if (left
->isRope()) {
469 JS_ASSERT(!Tagged(str
->u
.left
) && !Tagged(str
->s
.right
));
470 str
->u
.left
= Tag(parent
);
473 goto first_visit_node
;
475 JS_ASSERT_IF(!JSString::isStatic(left
),
476 strComp
== left
->asCell()->compartment()
477 || left
->asCell()->compartment() == rt
->atomsCompartment
);
478 NonRopeTypedMarker(rt
, left
);
481 JSString
*right
= str
->ropeRight();
482 if (right
->isRope()) {
483 JS_ASSERT(!Tagged(str
->u
.left
) && !Tagged(str
->s
.right
));
484 str
->s
.right
= Tag(parent
);
487 goto first_visit_node
;
489 JS_ASSERT_IF(!JSString::isStatic(right
),
490 strComp
== right
->asCell()->compartment()
491 || right
->asCell()->compartment() == rt
->atomsCompartment
);
492 NonRopeTypedMarker(rt
, right
);
497 if (Tagged(parent
->u
.left
)) {
498 JS_ASSERT(!Tagged(parent
->s
.right
));
499 JSString
*nextParent
= Untag(parent
->u
.left
);
500 parent
->u
.left
= str
;
503 goto visit_right_child
;
505 JS_ASSERT(Tagged(parent
->s
.right
));
506 JSString
*nextParent
= Untag(parent
->s
.right
);
507 parent
->s
.right
= str
;
515 MarkAtomRange(JSTracer
*trc
, size_t len
, JSAtom
**vec
, const char *name
)
517 for (uint32 i
= 0; i
< len
; i
++) {
518 if (JSAtom
*atom
= vec
[i
]) {
519 JS_SET_TRACING_INDEX(trc
, name
, i
);
520 JSString
*str
= ATOM_TO_STRING(atom
);
521 if (!JSString::isStatic(str
))
528 MarkObjectRange(JSTracer
*trc
, size_t len
, JSObject
**vec
, const char *name
)
530 for (uint32 i
= 0; i
< len
; i
++) {
531 if (JSObject
*obj
= vec
[i
]) {
532 JS_SET_TRACING_INDEX(trc
, name
, i
);
539 MarkId(JSTracer
*trc
, jsid id
)
541 if (JSID_IS_STRING(id
)) {
542 JSString
*str
= JSID_TO_STRING(id
);
543 if (!JSString::isStatic(str
))
546 else if (JS_UNLIKELY(JSID_IS_OBJECT(id
)))
547 Mark(trc
, JSID_TO_OBJECT(id
));
551 MarkId(JSTracer
*trc
, jsid id
, const char *name
)
553 JS_SET_TRACING_NAME(trc
, name
);
558 MarkIdRange(JSTracer
*trc
, jsid
*beg
, jsid
*end
, const char *name
)
560 for (jsid
*idp
= beg
; idp
!= end
; ++idp
) {
561 JS_SET_TRACING_INDEX(trc
, name
, (idp
- beg
));
567 MarkIdRange(JSTracer
*trc
, size_t len
, jsid
*vec
, const char *name
)
569 MarkIdRange(trc
, vec
, vec
+ len
, name
);
573 MarkKind(JSTracer
*trc
, void *thing
, uint32 kind
)
576 JS_ASSERT(kind
== GetGCThingTraceKind(thing
));
579 Mark(trc
, reinterpret_cast<JSObject
*>(thing
));
582 MarkString(trc
, reinterpret_cast<JSString
*>(thing
));
584 #if JS_HAS_XML_SUPPORT
586 Mark(trc
, reinterpret_cast<JSXML
*>(thing
));
594 /* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */
596 MarkValueRaw(JSTracer
*trc
, const js::Value
&v
)
598 if (v
.isMarkable()) {
599 JS_ASSERT(v
.toGCThing());
600 return MarkKind(trc
, v
.toGCThing(), v
.gcKind());
605 MarkValue(JSTracer
*trc
, const js::Value
&v
, const char *name
)
607 JS_SET_TRACING_NAME(trc
, name
);
608 MarkValueRaw(trc
, v
);
612 MarkValueRange(JSTracer
*trc
, Value
*beg
, Value
*end
, const char *name
)
614 for (Value
*vp
= beg
; vp
< end
; ++vp
) {
615 JS_SET_TRACING_INDEX(trc
, name
, vp
- beg
);
616 MarkValueRaw(trc
, *vp
);
621 MarkValueRange(JSTracer
*trc
, size_t len
, Value
*vec
, const char *name
)
623 MarkValueRange(trc
, vec
, vec
+ len
, name
);
627 MarkShapeRange(JSTracer
*trc
, const Shape
**beg
, const Shape
**end
, const char *name
)
629 for (const Shape
**sp
= beg
; sp
< end
; ++sp
) {
630 JS_SET_TRACING_INDEX(trc
, name
, sp
- beg
);
636 MarkShapeRange(JSTracer
*trc
, size_t len
, const Shape
**vec
, const char *name
)
638 MarkShapeRange(trc
, vec
, vec
+ len
, name
);
641 /* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */
643 MarkGCThing(JSTracer
*trc
, void *thing
, uint32 kind
)
648 MarkKind(trc
, thing
, kind
);
652 MarkGCThing(JSTracer
*trc
, void *thing
)
656 MarkKind(trc
, thing
, GetGCThingTraceKind(thing
));
660 MarkGCThing(JSTracer
*trc
, void *thing
, const char *name
)
662 JS_SET_TRACING_NAME(trc
, name
);
663 MarkGCThing(trc
, thing
);
667 MarkGCThing(JSTracer
*trc
, void *thing
, const char *name
, size_t index
)
669 JS_SET_TRACING_INDEX(trc
, name
, index
);
670 MarkGCThing(trc
, thing
);
674 Mark(JSTracer
*trc
, void *thing
, uint32 kind
, const char *name
)
677 JS_SET_TRACING_NAME(trc
, name
);
678 MarkKind(trc
, thing
, kind
);
683 #endif /* jsgcinlines_h___ */