Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / js / src / jsscope.cpp
blob5e3d42c2a6f0c7c202f49ed43af60bec8ee0d640
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JS symbol tables.
44 #include <new>
45 #include <stdlib.h>
46 #include <string.h>
47 #include "jstypes.h"
48 #include "jsstdint.h"
49 #include "jsarena.h"
50 #include "jsbit.h"
51 #include "jsclist.h"
52 #include "jsdhash.h"
53 #include "jsutil.h"
54 #include "jsapi.h"
55 #include "jsatom.h"
56 #include "jscntxt.h"
57 #include "jsdbgapi.h"
58 #include "jsfun.h" /* for JS_ARGS_LENGTH_MAX */
59 #include "jslock.h"
60 #include "jsnum.h"
61 #include "jsobj.h"
62 #include "jsscope.h"
63 #include "jsstr.h"
64 #include "jstracer.h"
66 #include "jsdbgapiinlines.h"
67 #include "jsobjinlines.h"
68 #include "jsscopeinlines.h"
70 using namespace js;
71 using namespace js::gc;
73 uint32
74 js_GenerateShape(JSRuntime *rt)
76 uint32 shape;
78 shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
79 JS_ASSERT(shape != 0);
80 if (shape >= SHAPE_OVERFLOW_BIT) {
82 * FIXME bug 440834: The shape id space has overflowed. Currently we
83 * cope badly with this and schedule the GC on the every call. But
84 * first we make sure that increments from other threads would not
85 * have a chance to wrap around shapeGen to zero.
87 rt->shapeGen = SHAPE_OVERFLOW_BIT;
88 shape = SHAPE_OVERFLOW_BIT;
90 #ifdef JS_THREADSAFE
91 AutoLockGC lockIf(rt);
92 #endif
93 TriggerGC(rt);
95 return shape;
98 uint32
99 js_GenerateShape(JSContext *cx)
101 return js_GenerateShape(cx->runtime);
104 bool
105 JSObject::ensureClassReservedSlotsForEmptyObject(JSContext *cx)
107 JS_ASSERT(nativeEmpty());
110 * Subtle rule: objects that call JSObject::ensureInstanceReservedSlots
111 * must either:
113 * (a) never escape anywhere an ad-hoc property could be set on them; or
115 * (b) protect their instance-reserved slots with shapes, at least a custom
116 * empty shape with the right slotSpan member.
118 * Block objects are the only objects that fall into category (a). While
119 * Call objects cannot escape, they can grow ad-hoc properties via eval
120 * of a var declaration, or due to a function statement being evaluated,
121 * but they have slots mapped by compiler-created shapes, and thus (b) no
122 * problem predicting first ad-hoc property slot. Bound Function objects
123 * have a custom empty shape.
125 * (Note that Block, Call, and bound Function objects are the only native
126 * class objects that are allowed to call ensureInstanceReservedSlots.)
128 uint32 nfixed = JSSLOT_FREE(getClass());
129 if (nfixed > numSlots() && !allocSlots(cx, nfixed))
130 return false;
132 return true;
135 #define PROPERTY_TABLE_NBYTES(n) ((n) * sizeof(Shape *))
137 #ifdef DEBUG
138 JS_FRIEND_DATA(JSScopeStats) js_scope_stats = {0};
140 # define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x)
141 #else
142 # define METER(x) ((void) 0)
143 #endif
145 bool
146 PropertyTable::init(JSRuntime *rt, Shape *lastProp)
149 * Either we're creating a table for a large scope that was populated
150 * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or
151 * JSOP_SETPROP; or else calloc failed at least once already. In any
152 * event, let's try to grow, overallocating to hold at least twice the
153 * current population.
155 uint32 sizeLog2 = JS_CeilingLog2(2 * entryCount);
156 if (sizeLog2 < MIN_SIZE_LOG2)
157 sizeLog2 = MIN_SIZE_LOG2;
160 * Use rt->calloc for memory accounting and overpressure handling
161 * without OOM reporting. See PropertyTable::change.
163 entries = (Shape **) rt->calloc(JS_BIT(sizeLog2) * sizeof(Shape *));
164 if (!entries) {
165 METER(tableAllocFails);
166 return false;
169 hashShift = JS_DHASH_BITS - sizeLog2;
170 for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) {
171 const Shape &shape = r.front();
172 METER(searches);
173 METER(initSearches);
174 Shape **spp = search(shape.id, true);
177 * Beware duplicate args and arg vs. var conflicts: the youngest shape
178 * (nearest to lastProp) must win. See bug 600067.
180 if (!SHAPE_FETCH(spp))
181 SHAPE_STORE_PRESERVING_COLLISION(spp, &shape);
183 return true;
186 bool
187 Shape::hashify(JSRuntime *rt)
189 JS_ASSERT(!hasTable());
190 void* mem = rt->malloc(sizeof(PropertyTable));
191 if (!mem)
192 return false;
193 setTable(new(mem) PropertyTable(entryCount()));
194 return getTable()->init(rt, this);
197 #ifdef DEBUG
198 # include "jsprf.h"
199 # define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr)
200 #else
201 # define LIVE_SCOPE_METER(cx,expr) /* nothing */
202 #endif
204 static inline bool
205 InitField(JSCompartment *comp, EmptyShape *JSCompartment:: *field, Class *clasp)
207 if (EmptyShape *emptyShape = EmptyShape::create(comp, clasp)) {
208 comp->*field = emptyShape;
209 return true;
211 return false;
214 /* static */
215 bool
216 Shape::initEmptyShapes(JSCompartment *comp)
219 * NewArguments allocates dslots to have enough room for the argc of the
220 * particular arguments object being created.
221 * never mutated, it's safe to pretend to have all the slots possible.
223 * Note how the fast paths in jsinterp.cpp for JSOP_LENGTH and JSOP_GETELEM
224 * bypass resolution of scope properties for length and element indices on
225 * arguments objects. This helps ensure that any arguments object needing
226 * its own mutable scope (with unique shape) is a rare event.
228 if (!InitField(comp, &JSCompartment::emptyArgumentsShape, &js_ArgumentsClass))
229 return false;
231 if (!InitField(comp, &JSCompartment::emptyBlockShape, &js_BlockClass))
232 return false;
235 * Initialize the shared scope for all empty Call objects so gets for args
236 * and vars do not force the creation of a mutable scope for the particular
237 * call object being accessed.
239 if (!InitField(comp, &JSCompartment::emptyCallShape, &js_CallClass))
240 return false;
242 /* A DeclEnv object holds the name binding for a named function expression. */
243 if (!InitField(comp, &JSCompartment::emptyDeclEnvShape, &js_DeclEnvClass))
244 return false;
246 /* Non-escaping native enumerator objects share this empty scope. */
247 if (!InitField(comp, &JSCompartment::emptyEnumeratorShape, &js_IteratorClass))
248 return false;
250 /* Same drill for With objects. */
251 if (!InitField(comp, &JSCompartment::emptyWithShape, &js_WithClass))
252 return false;
254 return true;
257 /* static */
258 void
259 Shape::finishEmptyShapes(JSCompartment *comp)
261 comp->emptyArgumentsShape = NULL;
262 comp->emptyBlockShape = NULL;
263 comp->emptyCallShape = NULL;
264 comp->emptyDeclEnvShape = NULL;
265 comp->emptyEnumeratorShape = NULL;
266 comp->emptyWithShape = NULL;
269 JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4);
270 JS_STATIC_ASSERT(sizeof(jsid) == JS_BYTES_PER_WORD);
272 #if JS_BYTES_PER_WORD == 4
273 # define HASH_ID(id) ((JSHashNumber)(JSID_BITS(id)))
274 #elif JS_BYTES_PER_WORD == 8
275 # define HASH_ID(id) ((JSHashNumber)(JSID_BITS(id)) ^ (JSHashNumber)((JSID_BITS(id)) >> 32))
276 #else
277 # error "Unsupported configuration"
278 #endif
281 * Double hashing needs the second hash code to be relatively prime to table
282 * size, so we simply make hash2 odd. The inputs to multiplicative hash are
283 * the golden ratio, expressed as a fixed-point 32 bit fraction, and the id
284 * itself.
286 #define HASH0(id) (HASH_ID(id) * JS_GOLDEN_RATIO)
287 #define HASH1(hash0,shift) ((hash0) >> (shift))
288 #define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1)
290 Shape **
291 PropertyTable::search(jsid id, bool adding)
293 JSHashNumber hash0, hash1, hash2;
294 int sizeLog2;
295 Shape *stored, *shape, **spp, **firstRemoved;
296 uint32 sizeMask;
298 JS_ASSERT(entries);
299 JS_ASSERT(!JSID_IS_VOID(id));
301 /* Compute the primary hash address. */
302 METER(hashes);
303 hash0 = HASH0(id);
304 hash1 = HASH1(hash0, hashShift);
305 spp = entries + hash1;
307 /* Miss: return space for a new entry. */
308 stored = *spp;
309 if (SHAPE_IS_FREE(stored)) {
310 METER(misses);
311 METER(hashMisses);
312 return spp;
315 /* Hit: return entry. */
316 shape = SHAPE_CLEAR_COLLISION(stored);
317 if (shape && shape->id == id) {
318 METER(hits);
319 METER(hashHits);
320 return spp;
323 /* Collision: double hash. */
324 sizeLog2 = JS_DHASH_BITS - hashShift;
325 hash2 = HASH2(hash0, sizeLog2, hashShift);
326 sizeMask = JS_BITMASK(sizeLog2);
328 #ifdef DEBUG
329 jsuword collision_flag = SHAPE_COLLISION;
330 #endif
332 /* Save the first removed entry pointer so we can recycle it if adding. */
333 if (SHAPE_IS_REMOVED(stored)) {
334 firstRemoved = spp;
335 } else {
336 firstRemoved = NULL;
337 if (adding && !SHAPE_HAD_COLLISION(stored))
338 SHAPE_FLAG_COLLISION(spp, shape);
339 #ifdef DEBUG
340 collision_flag &= jsuword(*spp) & SHAPE_COLLISION;
341 #endif
344 for (;;) {
345 METER(steps);
346 hash1 -= hash2;
347 hash1 &= sizeMask;
348 spp = entries + hash1;
350 stored = *spp;
351 if (SHAPE_IS_FREE(stored)) {
352 METER(misses);
353 METER(stepMisses);
354 return (adding && firstRemoved) ? firstRemoved : spp;
357 shape = SHAPE_CLEAR_COLLISION(stored);
358 if (shape && shape->id == id) {
359 METER(hits);
360 METER(stepHits);
361 JS_ASSERT(collision_flag);
362 return spp;
365 if (SHAPE_IS_REMOVED(stored)) {
366 if (!firstRemoved)
367 firstRemoved = spp;
368 } else {
369 if (adding && !SHAPE_HAD_COLLISION(stored))
370 SHAPE_FLAG_COLLISION(spp, shape);
371 #ifdef DEBUG
372 collision_flag &= jsuword(*spp) & SHAPE_COLLISION;
373 #endif
377 /* NOTREACHED */
378 return NULL;
381 bool
382 PropertyTable::change(int log2Delta, JSContext *cx)
384 int oldlog2, newlog2;
385 uint32 oldsize, newsize, nbytes;
386 Shape **newTable, **oldTable, **spp, **oldspp, *shape;
388 JS_ASSERT(entries);
391 * Grow, shrink, or compress by changing this->entries. Here, we prefer
392 * cx->runtime->calloc to js_calloc, which on OOM waits for a background
393 * thread to finish sweeping and retry, if appropriate. Avoid cx->calloc
394 * so our caller can be in charge of whether to JS_ReportOutOfMemory.
396 oldlog2 = JS_DHASH_BITS - hashShift;
397 newlog2 = oldlog2 + log2Delta;
398 oldsize = JS_BIT(oldlog2);
399 newsize = JS_BIT(newlog2);
400 nbytes = PROPERTY_TABLE_NBYTES(newsize);
401 newTable = (Shape **) cx->runtime->calloc(nbytes);
402 if (!newTable) {
403 METER(tableAllocFails);
404 return false;
407 /* Now that we have newTable allocated, update members. */
408 hashShift = JS_DHASH_BITS - newlog2;
409 removedCount = 0;
410 oldTable = entries;
411 entries = newTable;
413 /* Copy only live entries, leaving removed and free ones behind. */
414 for (oldspp = oldTable; oldsize != 0; oldspp++) {
415 shape = SHAPE_FETCH(oldspp);
416 if (shape) {
417 METER(searches);
418 METER(changeSearches);
419 spp = search(shape->id, true);
420 JS_ASSERT(SHAPE_IS_FREE(*spp));
421 *spp = shape;
423 oldsize--;
427 * Finally, free the old entries storage. Note that cx->runtime->free just
428 * calls js_free. Use js_free here to match PropertyTable::~PropertyTable,
429 * which cannot have a cx or rt parameter.
431 js_free(oldTable);
432 return true;
435 bool
436 PropertyTable::grow(JSContext *cx)
438 JS_ASSERT(needsToGrow());
440 uint32 size = capacity();
441 int delta = removedCount < size >> 2;
442 if (!delta)
443 METER(compresses);
444 else
445 METER(grows);
447 if (!change(delta, cx) && entryCount + removedCount == size - 1) {
448 JS_ReportOutOfMemory(cx);
449 return false;
451 return true;
454 Shape *
455 Shape::getChild(JSContext *cx, const js::Shape &child, Shape **listp)
457 JS_ASSERT(!JSID_IS_VOID(child.id));
458 JS_ASSERT(!child.inDictionary());
460 if (inDictionary()) {
461 Shape *oldShape = *listp;
462 PropertyTable *table = (oldShape && oldShape->hasTable()) ? oldShape->getTable() : NULL;
465 * Attempt to grow table if needed before extending *listp, rather than
466 * risking OOM under table->grow after newDictionaryShape succeeds, and
467 * then have to fix up *listp.
469 if (table && table->needsToGrow() && !table->grow(cx))
470 return NULL;
472 if (newDictionaryShape(cx, child, listp)) {
473 Shape *newShape = *listp;
475 JS_ASSERT(oldShape == newShape->parent);
476 if (table) {
477 /* Add newShape to the property table. */
478 METER(searches);
479 Shape **spp = table->search(newShape->id, true);
482 * Beware duplicate formal parameters, allowed by ECMA-262 in
483 * non-strict mode. Otherwise we know that Bindings::add (our
484 * caller) won't pass an id already in the table to us. In the
485 * case of duplicate formals, the last one wins, so while we
486 * must not overcount entries, we must store newShape.
488 if (!SHAPE_FETCH(spp))
489 ++table->entryCount;
490 SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
492 /* Hand the table off from oldShape to newShape. */
493 oldShape->setTable(NULL);
494 newShape->setTable(table);
495 } else {
496 if (!newShape->hasTable())
497 newShape->hashify(cx->runtime);
499 return newShape;
502 return NULL;
505 if ((*listp)->entryCount() >= PropertyTree::MAX_HEIGHT) {
506 Shape *dprop = Shape::newDictionaryList(cx, listp);
507 if (!dprop)
508 return NULL;
509 return dprop->getChild(cx, child, listp);
512 Shape *shape = JS_PROPERTY_TREE(cx).getChild(cx, this, child);
513 if (shape) {
514 JS_ASSERT(shape->parent == this);
515 JS_ASSERT(this == *listp);
516 *listp = shape;
518 return shape;
522 * Get or create a property-tree or dictionary child property of parent, which
523 * must be lastProp if inDictionaryMode(), else parent must be one of lastProp
524 * or lastProp->parent.
526 Shape *
527 JSObject::getChildProperty(JSContext *cx, Shape *parent, Shape &child)
529 JS_ASSERT(!JSID_IS_VOID(child.id));
530 JS_ASSERT(!child.inDictionary());
533 * Aliases share another property's slot, passed in the |slot| parameter.
534 * Shared properties have no slot. Unshared properties that do not alias
535 * another property's slot allocate a slot here, but may lose it due to a
536 * JS_ClearScope call.
538 if (!child.isAlias()) {
539 if (child.attrs & JSPROP_SHARED) {
540 child.slot = SHAPE_INVALID_SLOT;
541 } else {
543 * We may have set slot from a nearly-matching shape, above. If so,
544 * we're overwriting that nearly-matching shape, so we can reuse
545 * its slot -- we don't need to allocate a new one. Similarly, we
546 * use a specific slot if provided by the caller.
548 if (child.slot == SHAPE_INVALID_SLOT && !allocSlot(cx, &child.slot))
549 return NULL;
553 Shape *shape;
555 if (inDictionaryMode()) {
556 JS_ASSERT(parent == lastProp);
557 if (parent->frozen()) {
558 parent = Shape::newDictionaryList(cx, &lastProp);
559 if (!parent)
560 return NULL;
561 JS_ASSERT(!parent->frozen());
563 shape = Shape::newDictionaryShape(cx, child, &lastProp);
564 if (!shape)
565 return NULL;
566 } else {
567 shape = JS_PROPERTY_TREE(cx).getChild(cx, parent, child);
568 if (!shape)
569 return NULL;
570 JS_ASSERT(shape->parent == parent);
571 JS_ASSERT_IF(parent != lastProp, parent == lastProp->parent);
572 setLastProperty(shape);
575 updateFlags(shape);
576 updateShape(cx);
577 return shape;
580 Shape *
581 Shape::newDictionaryShape(JSContext *cx, const Shape &child, Shape **listp)
583 Shape *dprop = JS_PROPERTY_TREE(cx).newShape(cx);
584 if (!dprop)
585 return NULL;
587 new (dprop) Shape(child.id, child.rawGetter, child.rawSetter, child.slot, child.attrs,
588 (child.flags & ~FROZEN) | IN_DICTIONARY, child.shortid,
589 js_GenerateShape(cx), child.slotSpan);
591 dprop->listp = NULL;
592 dprop->insertIntoDictionary(listp);
594 JS_COMPARTMENT_METER(cx->compartment->liveDictModeNodes++);
595 return dprop;
598 Shape *
599 Shape::newDictionaryList(JSContext *cx, Shape **listp)
601 Shape *shape = *listp;
602 Shape *list = shape;
604 Shape **childp = listp;
605 *childp = NULL;
607 while (shape) {
608 JS_ASSERT_IF(!shape->frozen(), !shape->inDictionary());
610 Shape *dprop = Shape::newDictionaryShape(cx, *shape, childp);
611 if (!dprop) {
612 METER(toDictFails);
613 *listp = list;
614 return NULL;
617 JS_ASSERT(!dprop->hasTable());
618 childp = &dprop->parent;
619 shape = shape->parent;
622 list = *listp;
623 JS_ASSERT(list->inDictionary());
624 list->hashify(cx->runtime);
625 return list;
628 bool
629 JSObject::toDictionaryMode(JSContext *cx)
631 JS_ASSERT(!inDictionaryMode());
632 if (!Shape::newDictionaryList(cx, &lastProp))
633 return false;
635 clearOwnShape();
636 return true;
640 * Normalize stub getter and setter values for faster is-stub testing in the
641 * SHAPE_CALL_[GS]ETTER macros.
643 static inline bool
644 NormalizeGetterAndSetter(JSContext *cx, JSObject *obj,
645 jsid id, uintN attrs, uintN flags,
646 PropertyOp &getter,
647 StrictPropertyOp &setter)
649 if (setter == StrictPropertyStub) {
650 JS_ASSERT(!(attrs & JSPROP_SETTER));
651 setter = NULL;
653 if (flags & Shape::METHOD) {
654 /* Here, getter is the method, a function object reference. */
655 JS_ASSERT(getter);
656 JS_ASSERT(!setter || setter == js_watch_set);
657 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
658 } else {
659 if (getter == PropertyStub) {
660 JS_ASSERT(!(attrs & JSPROP_GETTER));
661 getter = NULL;
665 return true;
668 #ifdef DEBUG
669 # define CHECK_SHAPE_CONSISTENCY(obj) obj->checkShapeConsistency()
671 void
672 JSObject::checkShapeConsistency()
674 static int throttle = -1;
675 if (throttle < 0) {
676 if (const char *var = getenv("JS_CHECK_SHAPE_THROTTLE"))
677 throttle = atoi(var);
678 if (throttle < 0)
679 throttle = 0;
681 if (throttle == 0)
682 return;
684 JS_ASSERT(isNative());
685 if (hasOwnShape())
686 JS_ASSERT(objShape != lastProp->shape);
687 else
688 JS_ASSERT(objShape == lastProp->shape);
690 Shape *shape = lastProp;
691 Shape *prev = NULL;
693 if (inDictionaryMode()) {
694 if (shape->hasTable()) {
695 PropertyTable *table = shape->getTable();
696 for (uint32 fslot = table->freelist; fslot != SHAPE_INVALID_SLOT;
697 fslot = getSlotRef(fslot).toPrivateUint32()) {
698 JS_ASSERT(fslot < shape->slotSpan);
701 for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
702 JS_ASSERT_IF(shape != lastProp, !shape->hasTable());
704 Shape **spp = table->search(shape->id, false);
705 JS_ASSERT(SHAPE_FETCH(spp) == shape);
707 } else {
708 shape = shape->parent;
709 for (int n = throttle; --n >= 0 && shape; shape = shape->parent)
710 JS_ASSERT(!shape->hasTable());
713 shape = lastProp;
714 for (int n = throttle; --n >= 0 && shape; shape = shape->parent) {
715 JS_ASSERT_IF(shape->slot != SHAPE_INVALID_SLOT, shape->slot < shape->slotSpan);
716 if (!prev) {
717 JS_ASSERT(shape == lastProp);
718 JS_ASSERT(shape->listp == &lastProp);
719 } else {
720 JS_ASSERT(shape->listp == &prev->parent);
721 JS_ASSERT(prev->slotSpan >= shape->slotSpan);
723 prev = shape;
725 } else {
726 for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
727 if (shape->hasTable()) {
728 PropertyTable *table = shape->getTable();
729 JS_ASSERT(shape->parent);
730 for (Shape::Range r(shape); !r.empty(); r.popFront()) {
731 Shape **spp = table->search(r.front().id, false);
732 JS_ASSERT(SHAPE_FETCH(spp) == &r.front());
735 if (prev) {
736 JS_ASSERT(prev->slotSpan >= shape->slotSpan);
737 shape->kids.checkConsistency(prev);
739 prev = shape;
743 #else
744 # define CHECK_SHAPE_CONSISTENCY(obj) ((void)0)
745 #endif
747 const Shape *
748 JSObject::addProperty(JSContext *cx, jsid id,
749 PropertyOp getter, StrictPropertyOp setter,
750 uint32 slot, uintN attrs,
751 uintN flags, intN shortid)
753 JS_ASSERT(!JSID_IS_VOID(id));
755 if (!isExtensible()) {
756 reportNotExtensible(cx);
757 return NULL;
760 NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
762 /* Search for id with adding = true in order to claim its entry. */
763 Shape **spp = nativeSearch(id, true);
764 JS_ASSERT(!SHAPE_FETCH(spp));
765 const Shape *shape = addPropertyInternal(cx, id, getter, setter, slot, attrs,
766 flags, shortid, spp);
767 if (!shape)
768 return NULL;
770 /* Update any watchpoints referring to this property. */
771 shape = js_UpdateWatchpointsForShape(cx, this, shape);
772 if (!shape)
773 METER(wrapWatchFails);
775 return shape;
778 const Shape *
779 JSObject::addPropertyInternal(JSContext *cx, jsid id,
780 PropertyOp getter, StrictPropertyOp setter,
781 uint32 slot, uintN attrs,
782 uintN flags, intN shortid,
783 Shape **spp)
785 JS_ASSERT_IF(inDictionaryMode(), !lastProp->frozen());
787 PropertyTable *table = NULL;
788 if (!inDictionaryMode()) {
789 if (lastProp->entryCount() >= PropertyTree::MAX_HEIGHT) {
790 if (!toDictionaryMode(cx))
791 return NULL;
792 spp = nativeSearch(id, true);
793 table = lastProp->getTable();
795 } else if (lastProp->hasTable()) {
796 table = lastProp->getTable();
797 if (table->needsToGrow()) {
798 if (!table->grow(cx))
799 return NULL;
801 METER(searches);
802 METER(changeSearches);
803 spp = table->search(id, true);
804 JS_ASSERT(!SHAPE_FETCH(spp));
808 /* Find or create a property tree node labeled by our arguments. */
809 const Shape *shape;
811 Shape child(id, getter, setter, slot, attrs, flags, shortid);
812 shape = getChildProperty(cx, lastProp, child);
815 if (shape) {
816 JS_ASSERT(shape == lastProp);
818 if (table) {
819 /* Store the tree node pointer in the table entry for id. */
820 SHAPE_STORE_PRESERVING_COLLISION(spp, shape);
821 ++table->entryCount;
823 /* Pass the table along to the new lastProp, namely shape. */
824 JS_ASSERT(shape->parent->getTable() == table);
825 shape->parent->setTable(NULL);
826 shape->setTable(table);
828 #ifdef DEBUG
829 LIVE_SCOPE_METER(cx, ++cx->runtime->liveObjectProps);
830 #endif
831 CHECK_SHAPE_CONSISTENCY(this);
832 METER(adds);
833 return shape;
836 CHECK_SHAPE_CONSISTENCY(this);
837 METER(addFails);
838 return NULL;
842 * Check and adjust the new attributes for the shape to make sure that our
843 * slot access optimizations are sound. It is responsibility of the callers to
844 * enforce all restrictions from ECMA-262 v5 8.12.9 [[DefineOwnProperty]].
846 inline bool
847 CheckCanChangeAttrs(JSContext *cx, JSObject *obj, const Shape *shape, uintN *attrsp)
849 if (shape->configurable())
850 return true;
852 /* A permanent property must stay permanent. */
853 *attrsp |= JSPROP_PERMANENT;
855 /* Reject attempts to remove a slot from the permanent data property. */
856 if (shape->isDataDescriptor() && shape->hasSlot() &&
857 (*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED))) {
858 obj->reportNotConfigurable(cx, shape->id);
859 return false;
862 return true;
865 const Shape *
866 JSObject::putProperty(JSContext *cx, jsid id,
867 PropertyOp getter, StrictPropertyOp setter,
868 uint32 slot, uintN attrs,
869 uintN flags, intN shortid)
871 JS_ASSERT(!JSID_IS_VOID(id));
874 * Horrid non-strict eval, debuggers, and |default xml namespace ...| may
875 * extend Call objects.
877 if (lastProp->frozen()) {
878 if (!Shape::newDictionaryList(cx, &lastProp))
879 return NULL;
880 JS_ASSERT(!lastProp->frozen());
883 NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
885 /* Search for id in order to claim its entry if table has been allocated. */
886 Shape **spp = nativeSearch(id, true);
887 Shape *shape = SHAPE_FETCH(spp);
888 if (!shape) {
890 * You can't add properties to a non-extensible object, but you can change
891 * attributes of properties in such objects.
893 if (!isExtensible()) {
894 reportNotExtensible(cx);
895 return NULL;
898 const Shape *newShape =
899 addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp);
900 if (!newShape)
901 return NULL;
902 newShape = js_UpdateWatchpointsForShape(cx, this, newShape);
903 if (!newShape)
904 METER(wrapWatchFails);
905 return newShape;
908 /* Property exists: search must have returned a valid *spp. */
909 JS_ASSERT(!SHAPE_IS_REMOVED(*spp));
911 if (!CheckCanChangeAttrs(cx, this, shape, &attrs))
912 return NULL;
915 * If the caller wants to allocate a slot, but doesn't care which slot,
916 * copy the existing shape's slot into slot so we can match shape, if all
917 * other members match.
919 bool hadSlot = !shape->isAlias() && shape->hasSlot();
920 uint32 oldSlot = shape->slot;
921 if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot)
922 slot = oldSlot;
925 * Now that we've possibly preserved slot, check whether all members match.
926 * If so, this is a redundant "put" and we can return without more work.
928 if (shape->matchesParamsAfterId(getter, setter, slot, attrs, flags, shortid)) {
929 METER(redundantPuts);
930 return shape;
934 * Overwriting a non-last property requires switching to dictionary mode.
935 * The shape tree is shared immutable, and we can't removeProperty and then
936 * addPropertyInternal because a failure under add would lose data.
938 if (shape != lastProp && !inDictionaryMode()) {
939 if (!toDictionaryMode(cx))
940 return NULL;
941 spp = nativeSearch(shape->id);
942 shape = SHAPE_FETCH(spp);
946 * Now that we have passed the lastProp->frozen() check at the top of this
947 * method, and the non-last-property conditioning just above, we are ready
948 * to overwrite.
950 * Optimize the case of a non-frozen dictionary-mode object based on the
951 * property that dictionaries exclusively own their mutable shape structs,
952 * each of which has a unique shape number (not shared via a shape tree).
954 * This is more than an optimization: it is required to preserve for-in
955 * enumeration order (see bug 601399).
957 if (inDictionaryMode()) {
958 /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
959 if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED) && !(flags & Shape::ALIAS)) {
960 if (!allocSlot(cx, &slot))
961 return NULL;
964 shape->slot = slot;
965 if (slot != SHAPE_INVALID_SLOT && slot >= shape->slotSpan) {
966 shape->slotSpan = slot + 1;
968 for (Shape *temp = lastProp; temp != shape; temp = temp->parent) {
969 if (temp->slotSpan <= slot)
970 temp->slotSpan = slot + 1;
974 shape->rawGetter = getter;
975 shape->rawSetter = setter;
976 shape->attrs = uint8(attrs);
977 shape->flags = flags | Shape::IN_DICTIONARY;
978 shape->shortid = int16(shortid);
981 * We are done updating shape and lastProp. Now we may need to update
982 * flags and we will need to update objShape, which is no longer "own".
983 * In the last non-dictionary property case in the else clause just
984 * below, getChildProperty handles this for us. First update flags.
986 updateFlags(shape);
989 * We have just mutated shape in place, but nothing caches it based on
990 * shape->shape unless shape is lastProp and !hasOwnShape()). Therefore
991 * we regenerate only lastProp->shape. We will clearOwnShape(), which
992 * sets objShape to lastProp->shape.
994 lastProp->shape = js_GenerateShape(cx);
995 clearOwnShape();
996 } else {
998 * Updating lastProp in a non-dictionary-mode object. Such objects
999 * share their shapes via a tree rooted at a prototype emptyShape, or
1000 * perhaps a well-known compartment-wide singleton emptyShape.
1002 * If any shape in the tree has a property hashtable, it is shared and
1003 * immutable too, therefore we must not update *spp.
1005 JS_ASSERT(shape == lastProp);
1006 removeLastProperty();
1008 /* Find or create a property tree node labeled by our arguments. */
1009 Shape child(id, getter, setter, slot, attrs, flags, shortid);
1011 Shape *newShape = getChildProperty(cx, lastProp, child);
1012 if (!newShape) {
1013 setLastProperty(shape);
1014 CHECK_SHAPE_CONSISTENCY(this);
1015 METER(putFails);
1016 return NULL;
1019 shape = newShape;
1023 * Can't fail now, so free the previous incarnation's slot if the new shape
1024 * has no slot. But we do not need to free oldSlot (and must not, as trying
1025 * to will botch an assertion in JSObject::freeSlot) if the new lastProp
1026 * (shape here) has a slotSpan that does not cover it.
1028 if (hadSlot && !shape->hasSlot()) {
1029 if (oldSlot < shape->slotSpan)
1030 freeSlot(cx, oldSlot);
1031 #ifdef DEBUG
1032 else
1033 getSlotRef(oldSlot).setUndefined();
1034 #endif
1035 JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
1038 CHECK_SHAPE_CONSISTENCY(this);
1039 METER(puts);
1041 const Shape *newShape = js_UpdateWatchpointsForShape(cx, this, shape);
1042 if (!newShape)
1043 METER(wrapWatchFails);
1044 return newShape;
1047 const Shape *
1048 JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN mask,
1049 PropertyOp getter, StrictPropertyOp setter)
1051 JS_ASSERT_IF(inDictionaryMode(), !lastProp->frozen());
1052 JS_ASSERT(!JSID_IS_VOID(shape->id));
1053 JS_ASSERT(nativeContains(*shape));
1055 attrs |= shape->attrs & mask;
1057 /* Allow only shared (slotless) => unshared (slotful) transition. */
1058 JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
1059 !(attrs & JSPROP_SHARED));
1061 /* Don't allow method properties to be changed to have a getter. */
1062 JS_ASSERT_IF(getter != shape->rawGetter, !shape->isMethod());
1064 if (getter == PropertyStub)
1065 getter = NULL;
1066 if (setter == StrictPropertyStub)
1067 setter = NULL;
1069 if (!CheckCanChangeAttrs(cx, this, shape, &attrs))
1070 return NULL;
1072 if (shape->attrs == attrs && shape->getter() == getter && shape->setter() == setter)
1073 return shape;
1075 const Shape *newShape;
1078 * Dictionary-mode objects exclusively own their mutable shape structs, so
1079 * we simply modify in place.
1081 if (inDictionaryMode()) {
1082 /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
1083 uint32 slot = shape->slot;
1084 if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED) && !(flags & Shape::ALIAS)) {
1085 if (!allocSlot(cx, &slot))
1086 return NULL;
1089 Shape *mutableShape = const_cast<Shape *>(shape);
1090 mutableShape->slot = slot;
1091 if (slot != SHAPE_INVALID_SLOT && slot >= shape->slotSpan) {
1092 mutableShape->slotSpan = slot + 1;
1094 for (Shape *temp = lastProp; temp != shape; temp = temp->parent) {
1095 if (temp->slotSpan <= slot)
1096 temp->slotSpan = slot + 1;
1100 mutableShape->rawGetter = getter;
1101 mutableShape->rawSetter = setter;
1102 mutableShape->attrs = uint8(attrs);
1104 updateFlags(shape);
1106 /* See the corresponding code in putProperty. */
1107 lastProp->shape = js_GenerateShape(cx);
1108 clearOwnShape();
1110 shape = js_UpdateWatchpointsForShape(cx, this, shape);
1111 if (!shape) {
1112 METER(wrapWatchFails);
1113 return NULL;
1115 JS_ASSERT(shape == mutableShape);
1116 newShape = mutableShape;
1117 } else if (shape == lastProp) {
1118 Shape child(shape->id, getter, setter, shape->slot, attrs, shape->flags, shape->shortid);
1120 newShape = getChildProperty(cx, shape->parent, child);
1121 #ifdef DEBUG
1122 if (newShape) {
1123 JS_ASSERT(newShape == lastProp);
1124 if (newShape->hasTable()) {
1125 Shape **spp = nativeSearch(shape->id);
1126 JS_ASSERT(SHAPE_FETCH(spp) == newShape);
1129 #endif
1130 } else {
1132 * Let JSObject::putProperty handle this |overwriting| case, including
1133 * the conservation of shape->slot (if it's valid). We must not call
1134 * removeProperty because it will free an allocated shape->slot, and
1135 * putProperty won't re-allocate it.
1137 Shape child(shape->id, getter, setter, shape->slot, attrs, shape->flags, shape->shortid);
1138 newShape = putProperty(cx, child.id, child.rawGetter, child.rawSetter, child.slot,
1139 child.attrs, child.flags, child.shortid);
1140 #ifdef DEBUG
1141 if (newShape)
1142 METER(changePuts);
1143 #endif
1146 #ifdef DEBUG
1147 CHECK_SHAPE_CONSISTENCY(this);
1148 if (newShape)
1149 METER(changes);
1150 else
1151 METER(changeFails);
1152 #endif
1153 return newShape;
1156 bool
1157 JSObject::removeProperty(JSContext *cx, jsid id)
1159 Shape **spp = nativeSearch(id);
1160 Shape *shape = SHAPE_FETCH(spp);
1161 if (!shape) {
1162 METER(uselessRemoves);
1163 return true;
1166 /* First, if shape is unshared and not has a slot, free its slot number. */
1167 bool addedToFreelist = false;
1168 bool hadSlot = !shape->isAlias() && shape->hasSlot();
1169 if (hadSlot) {
1170 addedToFreelist = freeSlot(cx, shape->slot);
1171 JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
1175 /* If shape is not the last property added, switch to dictionary mode. */
1176 if (shape != lastProp && !inDictionaryMode()) {
1177 if (!toDictionaryMode(cx))
1178 return false;
1179 spp = nativeSearch(shape->id);
1180 shape = SHAPE_FETCH(spp);
1184 * A dictionary-mode object owns mutable, unique shapes on a non-circular
1185 * doubly linked list, optionally hashed by lastProp->table. So we can edit
1186 * the list and hash in place.
1188 if (inDictionaryMode()) {
1189 PropertyTable *table = lastProp->hasTable() ? lastProp->getTable() : NULL;
1191 if (SHAPE_HAD_COLLISION(*spp)) {
1192 JS_ASSERT(table);
1193 *spp = SHAPE_REMOVED;
1194 ++table->removedCount;
1195 --table->entryCount;
1196 } else {
1197 METER(removeFrees);
1198 if (table) {
1199 *spp = NULL;
1200 --table->entryCount;
1202 #ifdef DEBUG
1204 * Check the consistency of the table but limit the number of
1205 * checks not to alter significantly the complexity of the
1206 * delete in debug builds, see bug 534493.
1208 const Shape *aprop = lastProp;
1209 for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
1210 JS_ASSERT_IF(aprop != shape, nativeContains(*aprop));
1211 #endif
1216 * Remove shape from its non-circular doubly linked list, setting this
1217 * object's OWN_SHAPE flag so the updateShape(cx) further below will
1218 * generate a fresh shape id for this object, distinct from the id of
1219 * any shape in the list. We need a fresh shape for all deletions, even
1220 * of lastProp. Otherwise, a shape number could replay and caches might
1221 * return get deleted DictionaryShapes! See bug 595365.
1223 flags |= OWN_SHAPE;
1225 Shape *oldLastProp = lastProp;
1226 shape->removeFromDictionary(this);
1227 if (table) {
1228 if (shape == oldLastProp) {
1229 JS_ASSERT(shape->getTable() == table);
1230 JS_ASSERT(shape->parent == lastProp);
1231 JS_ASSERT(shape->slotSpan >= lastProp->slotSpan);
1232 JS_ASSERT_IF(hadSlot, shape->slot + 1 <= shape->slotSpan);
1235 * Maintain slot freelist consistency. The only constraint we
1236 * have is that slot numbers on the freelist are less than
1237 * lastProp->slotSpan. Thus, if the freelist is non-empty,
1238 * then lastProp->slotSpan may not decrease.
1240 if (table->freelist != SHAPE_INVALID_SLOT) {
1241 lastProp->slotSpan = shape->slotSpan;
1243 /* Add the slot to the freelist if it wasn't added in freeSlot. */
1244 if (hadSlot && !addedToFreelist) {
1245 getSlotRef(shape->slot).setPrivateUint32(table->freelist);
1246 table->freelist = shape->slot;
1251 /* Hand off table from old to new lastProp. */
1252 oldLastProp->setTable(NULL);
1253 lastProp->setTable(table);
1255 } else {
1257 * Non-dictionary-mode property tables are shared immutables, so all we
1258 * need do is retract lastProp and we'll either get or else lazily make
1259 * via a later hashify the exact table for the new property lineage.
1261 JS_ASSERT(shape == lastProp);
1262 removeLastProperty();
1265 * Revert to fixed slots if this was the first dynamically allocated slot,
1266 * preserving invariant that objects with the same shape use the fixed
1267 * slots in the same way.
1269 size_t fixed = numFixedSlots();
1270 if (shape->slot == fixed) {
1271 JS_ASSERT_IF(!lastProp->isEmptyShape() && lastProp->hasSlot(),
1272 lastProp->slot == fixed - 1);
1273 revertToFixedSlots(cx);
1276 updateShape(cx);
1278 /* On the way out, consider shrinking table if its load factor is <= .25. */
1279 if (lastProp->hasTable()) {
1280 PropertyTable *table = lastProp->getTable();
1281 uint32 size = table->capacity();
1282 if (size > PropertyTable::MIN_SIZE && table->entryCount <= size >> 2) {
1283 METER(shrinks);
1284 (void) table->change(-1, cx);
1288 CHECK_SHAPE_CONSISTENCY(this);
1289 LIVE_SCOPE_METER(cx, --cx->runtime->liveObjectProps);
1290 METER(removes);
1291 return true;
1294 void
1295 JSObject::clear(JSContext *cx)
1297 LIVE_SCOPE_METER(cx, cx->runtime->liveObjectProps -= propertyCount());
1299 Shape *shape = lastProp;
1300 JS_ASSERT(inDictionaryMode() == shape->inDictionary());
1302 while (shape->parent) {
1303 shape = shape->parent;
1304 JS_ASSERT(inDictionaryMode() == shape->inDictionary());
1306 JS_ASSERT(shape->isEmptyShape());
1308 if (inDictionaryMode())
1309 shape->listp = &lastProp;
1312 * Revert to fixed slots if we have cleared below the first dynamically
1313 * allocated slot, preserving invariant that objects with the same shape
1314 * use the fixed slots in the same way.
1316 if (hasSlotsArray() && JSSLOT_FREE(getClass()) <= numFixedSlots())
1317 revertToFixedSlots(cx);
1320 * We have rewound to a uniquely-shaped empty scope, so we don't need an
1321 * override for this object's shape.
1323 clearOwnShape();
1324 setMap(shape);
1326 LeaveTraceIfGlobalObject(cx, this);
1327 JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
1328 CHECK_SHAPE_CONSISTENCY(this);
1331 void
1332 JSObject::generateOwnShape(JSContext *cx)
1334 #ifdef JS_TRACER
1335 JS_ASSERT_IF(!parent && JS_ON_TRACE(cx), JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit);
1336 LeaveTraceIfGlobalObject(cx, this);
1339 * If we are recording, here is where we forget already-guarded shapes.
1340 * Any subsequent property operation upon object on the trace currently
1341 * being recorded will re-guard (and re-memoize).
1343 if (TraceRecorder *tr = TRACE_RECORDER(cx))
1344 tr->forgetGuardedShapesForObject(this);
1345 #endif
1347 setOwnShape(js_GenerateShape(cx));
1350 void
1351 JSObject::deletingShapeChange(JSContext *cx, const Shape &shape)
1353 JS_ASSERT(!JSID_IS_VOID(shape.id));
1354 generateOwnShape(cx);
1357 const Shape *
1358 JSObject::methodShapeChange(JSContext *cx, const Shape &shape)
1360 const Shape *result = &shape;
1362 JS_ASSERT(!JSID_IS_VOID(shape.id));
1363 if (shape.isMethod()) {
1364 #ifdef DEBUG
1365 const Value &prev = nativeGetSlot(shape.slot);
1366 JS_ASSERT(&shape.methodObject() == &prev.toObject());
1367 JS_ASSERT(canHaveMethodBarrier());
1368 JS_ASSERT(hasMethodBarrier());
1369 JS_ASSERT(!shape.rawSetter || shape.rawSetter == js_watch_set);
1370 #endif
1373 * Pass null to make a stub getter, but pass along shape.rawSetter to
1374 * preserve watchpoints. Clear Shape::METHOD from flags as we are
1375 * despecializing from a method memoized in the property tree to a
1376 * plain old function-valued property.
1378 result = putProperty(cx, shape.id, NULL, shape.rawSetter, shape.slot,
1379 shape.attrs,
1380 shape.getFlags() & ~Shape::METHOD,
1381 shape.shortid);
1382 if (!result)
1383 return NULL;
1386 if (branded()) {
1387 uintN thrashCount = getMethodThrashCount();
1388 if (thrashCount < JSObject::METHOD_THRASH_COUNT_MAX) {
1389 ++thrashCount;
1390 setMethodThrashCount(thrashCount);
1391 if (thrashCount == JSObject::METHOD_THRASH_COUNT_MAX) {
1392 unbrand(cx);
1393 return result;
1398 generateOwnShape(cx);
1399 return result;
1402 bool
1403 JSObject::methodShapeChange(JSContext *cx, uint32 slot)
1405 if (!hasMethodBarrier()) {
1406 generateOwnShape(cx);
1407 } else {
1408 for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) {
1409 const Shape &shape = r.front();
1410 JS_ASSERT(!JSID_IS_VOID(shape.id));
1411 if (shape.slot == slot)
1412 return methodShapeChange(cx, shape) != NULL;
1415 return true;
1418 void
1419 JSObject::protoShapeChange(JSContext *cx)
1421 generateOwnShape(cx);
1424 void
1425 JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape)
1427 JS_ASSERT(!JSID_IS_VOID(shape.id));
1428 generateOwnShape(cx);
1431 bool
1432 JSObject::globalObjectOwnShapeChange(JSContext *cx)
1434 generateOwnShape(cx);
1435 return !js_IsPropertyCacheDisabled(cx);
1438 #ifdef DEBUG
1439 static void
1440 PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize)
1442 Shape *shape;
1443 jsid id;
1444 size_t n;
1445 const char *name;
1447 JS_ASSERT(trc->debugPrinter == PrintPropertyGetterOrSetter);
1448 shape = (Shape *)trc->debugPrintArg;
1449 id = shape->id;
1450 JS_ASSERT(!JSID_IS_VOID(id));
1451 name = trc->debugPrintIndex ? js_setter_str : js_getter_str;
1453 if (JSID_IS_ATOM(id)) {
1454 n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0);
1455 if (n < bufsize)
1456 JS_snprintf(buf + n, bufsize - n, " %s", name);
1457 } else if (JSID_IS_INT(shape->id)) {
1458 JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(id), name);
1459 } else {
1460 JS_snprintf(buf, bufsize, "<object> %s", name);
1464 static void
1465 PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize)
1467 Shape *shape;
1468 jsid id;
1469 size_t n;
1471 JS_ASSERT(trc->debugPrinter == PrintPropertyMethod);
1472 shape = (Shape *)trc->debugPrintArg;
1473 id = shape->id;
1474 JS_ASSERT(!JSID_IS_VOID(id));
1476 JS_ASSERT(JSID_IS_ATOM(id));
1477 n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0);
1478 if (n < bufsize)
1479 JS_snprintf(buf + n, bufsize - n, " method");
1481 #endif
1483 void
1484 Shape::trace(JSTracer *trc) const
1486 #ifdef DEBUG
1487 JSRuntime *rt = trc->context->runtime;
1488 JS_ASSERT_IF(rt->gcCurrentCompartment, compartment == rt->gcCurrentCompartment);
1489 #endif
1491 if (IS_GC_MARKING_TRACER(trc))
1492 mark();
1494 MarkId(trc, id, "id");
1496 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1497 if ((attrs & JSPROP_GETTER) && rawGetter) {
1498 JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 0);
1499 Mark(trc, getterObject());
1501 if ((attrs & JSPROP_SETTER) && rawSetter) {
1502 JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 1);
1503 Mark(trc, setterObject());
1507 if (isMethod()) {
1508 JS_SET_TRACING_DETAILS(trc, PrintPropertyMethod, this, 0);
1509 Mark(trc, &methodObject());