1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
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
17 * The Original Code is Mozilla Communicator client code, released
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.
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 ***** */
56 #include "jspropertycache.h"
57 #include "jspropertytree.h"
61 #pragma warning(disable:4800)
63 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
67 * Given P independent, non-unique properties each of size S words mapped by
68 * all scopes in a runtime, construct a property tree of N nodes each of size
69 * S+L words (L for tree linkage). A nominal L value is 2 for leftmost-child
70 * and right-sibling links. We hope that the N < P by enough that the space
71 * overhead of L, and the overhead of scope entries pointing at property tree
74 * The tree construction goes as follows. If any empty scope in the runtime
75 * has a property X added to it, find or create a node under the tree root
76 * labeled X, and set scope->lastProp to point at that node. If any non-empty
77 * scope whose most recently added property is labeled Y has another property
78 * labeled Z added, find or create a node for Z under the node that was added
79 * for Y, and set scope->lastProp to point at that node.
81 * A property is labeled by its members' values: id, getter, setter, slot,
82 * attributes, tiny or short id, and a field telling for..in order. Note that
83 * labels are not unique in the tree, but they are unique among a node's kids
84 * (barring rare and benign multi-threaded race condition outcomes, see below)
85 * and along any ancestor line from the tree root to a given leaf node (except
86 * for the hard case of duplicate formal parameters to a function).
88 * Thus the root of the tree represents all empty scopes, and the first ply
89 * of the tree represents all scopes containing one property, etc. Each node
90 * in the tree can stand for any number of scopes having the same ordered set
91 * of properties, where that node was the last added to the scope. (We need
92 * not store the root of the tree as a node, and do not -- all we need are
95 * Sidebar on for..in loop order: ECMA requires no particular order, but this
96 * implementation has promised and delivered property definition order, and
97 * compatibility is king. We could use an order number per property, which
98 * would require a sort in js_Enumerate, and an entry order generation number
99 * per scope. An order number beats a list, which should be doubly-linked for
100 * O(1) delete. An even better scheme is to use a parent link in the property
101 * tree, so that the ancestor line can be iterated from scope->lastProp when
102 * filling in a JSIdArray from back to front. This parent link also helps the
103 * GC to sweep properties iteratively.
105 * What if a property Y is deleted from a scope? If Y is the last property in
106 * the scope, we simply adjust the scope's lastProp member after we remove the
107 * scope's hash-table entry pointing at that property node. The parent link
108 * mentioned in the for..in sidebar above makes this adjustment O(1). But if
109 * Y comes between X and Z in the scope, then we might have to "fork" the tree
110 * at X, leaving X->Y->Z in case other scopes have those properties added in
111 * that order; and to finish the fork, we'd add a node labeled Z with the path
112 * X->Z, if it doesn't exist. This could lead to lots of extra nodes, and to
113 * O(n^2) growth when deleting lots of properties.
115 * Rather, for O(1) growth all around, we should share the path X->Y->Z among
116 * scopes having those three properties added in that order, and among scopes
117 * having only X->Z where Y was deleted. All such scopes have a lastProp that
118 * points to the Z child of Y. But a scope in which Y was deleted does not
119 * have a table entry for Y, and when iterating that scope by traversing the
120 * ancestor line from Z, we will have to test for a table entry for each node,
121 * skipping nodes that lack entries.
123 * What if we add Y again? X->Y->Z->Y is wrong and we'll enumerate Y twice.
124 * Therefore we must fork in such a case if not earlier, or do something else.
125 * We used to fork on the theory that set after delete is rare, but the Web is
126 * a harsh mistress, and we now convert the scope to a "dictionary" on first
127 * delete, to avoid O(n^2) growth in the property tree.
129 * What about thread safety? If the property tree operations done by requests
130 * are find-node and insert-node, then the only hazard is duplicate insertion.
131 * This is harmless except for minor bloat. When all requests have ended or
132 * been suspended, the GC is free to sweep the tree after marking all nodes
133 * reachable from scopes, performing remove-node operations as needed.
135 * Is the property tree worth it compared to property storage in each table's
136 * entries? To decide, we must find the relation <> between the words used
137 * with a property tree and the words required without a tree.
139 * Model all scopes as one super-scope of capacity T entries (T a power of 2).
140 * Let alpha be the load factor of this double hash-table. With the property
141 * tree, each entry in the table is a word-sized pointer to a node that can be
142 * shared by many scopes. But all such pointers are overhead compared to the
143 * situation without the property tree, where the table stores property nodes
144 * directly, as entries each of size S words. With the property tree, we need
145 * L=2 extra words per node for siblings and kids pointers. Without the tree,
146 * (1-alpha)*S*T words are wasted on free or removed sentinel-entries required
151 * (property tree) <> (no property tree)
153 * N*(S+L) + T <> P*S + (1-alpha)*S*T
154 * N*(S+L) + alpha*T + (1-alpha)*T <> P*S + (1-alpha)*S*T
156 * Note that P is alpha*T by definition, so
158 * N*(S+L) + P + (1-alpha)*T <> P*S + (1-alpha)*S*T
159 * N*(S+L) <> P*S - P + (1-alpha)*S*T - (1-alpha)*T
160 * N*(S+L) <> (P + (1-alpha)*T) * (S-1)
161 * N*(S+L) <> (P + (1-alpha)*P/alpha) * (S-1)
162 * N*(S+L) <> P * (1/alpha) * (S-1)
164 * Let N = P*beta for a compression ratio beta, beta <= 1:
166 * P*beta*(S+L) <> P * (1/alpha) * (S-1)
167 * beta*(S+L) <> (S-1)/alpha
168 * beta <> (S-1)/((S+L)*alpha)
170 * For S = 6 (32-bit architectures) and L = 2, the property tree wins iff
174 * We ensure that alpha <= .75, so the property tree wins if beta < .83_. An
175 * average beta from recent Mozilla browser startups was around .6.
177 * Can we reduce L? Observe that the property tree degenerates into a list of
178 * lists if at most one property Y follows X in all scopes. In or near such a
179 * case, we waste a word on the right-sibling link outside of the root ply of
180 * the tree. Note also that the root ply tends to be large, so O(n^2) growth
181 * searching it is likely, indicating the need for hashing (but with increased
182 * thread safety costs).
184 * If only K out of N nodes in the property tree have more than one child, we
185 * could eliminate the sibling link and overlay a children list or hash-table
186 * pointer on the leftmost-child link (which would then be either null or an
187 * only-child link; the overlay could be tagged in the low bit of the pointer,
188 * or flagged elsewhere in the property tree node, although such a flag must
189 * not be considered when comparing node labels during tree search).
191 * For such a system, L = 1 + (K * averageChildrenTableSize) / N instead of 2.
192 * If K << N, L approaches 1 and the property tree wins if beta < .95.
194 * We observe that fan-out below the root ply of the property tree appears to
195 * have extremely low degree (see the MeterPropertyTree code that histograms
196 * child-counts in jsscope.c), so instead of a hash-table we use a linked list
197 * of child node pointer arrays ("kid chunks"). The details are isolated in
198 * jsscope.c; others must treat JSScopeProperty.kids as opaque. We leave it
199 * strongly typed for debug-ability of the common (null or one-kid) cases.
201 * One final twist (can you stand it?): the mean number of entries per scope
202 * in Mozilla is < 5, with a large standard deviation (~8). Instead of always
203 * allocating scope->table, we leave it null while initializing all the other
204 * scope members as if it were non-null and minimal-length. Until a property
205 * is added that crosses the threshold of 6 or more entries for hashing, we use
206 * linear search from scope->lastProp to find a given id, and save on the space
207 * overhead of a hash table.
209 * See jspropertytree.{h,cpp} for the actual PropertyTree implementation. This
210 * file contains object property map (historical misnomer: "scope" AKA JSScope)
211 * and property tree node ("sprop", JSScopeProperty) declarations.
216 #define SPROP_INVALID_SLOT 0xffffffff
218 struct JSScope
: public JSObjectMap
221 JSTitle title
; /* lock state */
223 JSObject
*object
; /* object that owns this scope */
224 uint32 freeslot
; /* index of next free slot in object */
226 uint8 flags
; /* flags, see below */
228 int8 hashShift
; /* multiplicative hash shift */
230 uint16 spare
; /* reserved */
231 uint32 entryCount
; /* number of entries in table */
232 uint32 removedCount
; /* removed entry sentinels in table */
233 JSScopeProperty
**table
; /* table of ptrs to shared tree nodes */
234 JSEmptyScope
*emptyScope
; /* cache for getEmptyScope below */
237 * A little information hiding for scope->lastProp, in case it ever becomes
238 * a tagged pointer again.
240 inline JSScopeProperty
*lastProperty() const;
243 JSScopeProperty
*getChildProperty(JSContext
*cx
, JSScopeProperty
*parent
,
244 JSScopeProperty
&child
);
246 JSScopeProperty
*newDictionaryProperty(JSContext
*cx
, const JSScopeProperty
&child
,
247 JSScopeProperty
**childp
);
249 bool toDictionaryMode(JSContext
*cx
, JSScopeProperty
*&aprop
);
252 * Private pointer to the last added property and methods to manipulate the
253 * list it links among properties in this scope. The {remove,insert} pair
254 * for DictionaryProperties assert that the scope is in dictionary mode and
255 * any reachable properties are flagged as dictionary properties.
257 * NB: these private methods do *not* update this scope's shape to track
258 * lastProp->shape after they finish updating the linked list in the case
259 * where lastProp is updated. It is up to calling code in jsscope.cpp to
260 * call updateShape(cx) after updating lastProp.
262 JSScopeProperty
*lastProp
;
264 /* These four inline methods are defined further below in this .h file. */
265 inline void setLastProperty(JSScopeProperty
*sprop
);
266 inline void removeLastProperty();
267 inline void removeDictionaryProperty(JSScopeProperty
*sprop
);
268 inline void insertDictionaryProperty(JSScopeProperty
*sprop
, JSScopeProperty
**childp
);
270 /* Defined in jsscopeinlines.h to avoid including implementation dependencies here. */
271 inline void updateShape(JSContext
*cx
);
272 inline void updateFlags(const JSScopeProperty
*sprop
, bool isDefinitelyAtom
= false);
275 void initMinimal(JSContext
*cx
, uint32 newShape
);
278 bool createTable(JSContext
*cx
, bool report
);
279 bool changeTable(JSContext
*cx
, int change
);
280 void reportReadOnlyScope(JSContext
*cx
);
282 void setOwnShape() { flags
|= OWN_SHAPE
; }
283 void clearOwnShape() { flags
&= ~OWN_SHAPE
; }
284 void generateOwnShape(JSContext
*cx
);
286 JSScopeProperty
**searchTable(jsid id
, bool adding
);
287 inline JSScopeProperty
**search(jsid id
, bool adding
);
288 inline JSEmptyScope
*createEmptyScope(JSContext
*cx
, js::Class
*clasp
);
290 JSScopeProperty
*addPropertyHelper(JSContext
*cx
, jsid id
,
291 js::PropertyOp getter
, js::PropertyOp setter
,
292 uint32 slot
, uintN attrs
,
293 uintN flags
, intN shortid
,
294 JSScopeProperty
**spp
);
297 JSScope(JSObject
*obj
)
298 : JSObjectMap(0), object(obj
) {}
300 /* Create a mutable, owned, empty scope. */
301 static JSScope
*create(JSContext
*cx
, js::Class
*clasp
, JSObject
*obj
, uint32 shape
);
303 void destroy(JSContext
*cx
);
306 * Return an immutable, shareable, empty scope with the same ops as this
307 * and the same freeslot as this had when empty.
309 * If |this| is the scope of an object |proto|, the resulting scope can be
310 * used as the scope of a new object whose prototype is |proto|.
312 inline JSEmptyScope
*getEmptyScope(JSContext
*cx
, js::Class
*clasp
);
314 inline bool ensureEmptyScope(JSContext
*cx
, js::Class
*clasp
);
316 inline bool canProvideEmptyScope(js::Class
*clasp
);
318 JSScopeProperty
*lookup(jsid id
);
320 inline bool hasProperty(jsid id
) { return lookup(id
) != NULL
; }
321 inline bool hasProperty(JSScopeProperty
*sprop
);
323 /* Add a property whose id is not yet in this scope. */
324 JSScopeProperty
*addProperty(JSContext
*cx
, jsid id
,
325 js::PropertyOp getter
, js::PropertyOp setter
,
326 uint32 slot
, uintN attrs
,
327 uintN flags
, intN shortid
);
329 /* Add a data property whose id is not yet in this scope. */
330 JSScopeProperty
*addDataProperty(JSContext
*cx
, jsid id
, uint32 slot
, uintN attrs
) {
331 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
332 return addProperty(cx
, id
, NULL
, NULL
, slot
, attrs
, 0, 0);
335 /* Add or overwrite a property for id in this scope. */
336 JSScopeProperty
*putProperty(JSContext
*cx
, jsid id
,
337 js::PropertyOp getter
, js::PropertyOp setter
,
338 uint32 slot
, uintN attrs
,
339 uintN flags
, intN shortid
);
341 /* Change the given property into a sibling with the same id in this scope. */
342 JSScopeProperty
*changeProperty(JSContext
*cx
, JSScopeProperty
*sprop
,
343 uintN attrs
, uintN mask
,
344 js::PropertyOp getter
, js::PropertyOp setter
);
346 /* Remove id from this scope. */
347 bool removeProperty(JSContext
*cx
, jsid id
);
349 /* Clear the scope, making it empty. */
350 void clear(JSContext
*cx
);
352 /* Extend this scope to have sprop as its last-added property. */
353 void extend(JSContext
*cx
, JSScopeProperty
*sprop
, bool isDefinitelyAtom
= false);
356 * Read barrier to clone a joined function object stored as a method.
357 * Defined in jsscopeinlines.h, but not declared inline per standard style
358 * in order to avoid gcc warnings.
360 bool methodReadBarrier(JSContext
*cx
, JSScopeProperty
*sprop
, js::Value
*vp
);
363 * Write barrier to check for a method value change. Defined inline below
364 * after methodReadBarrier. Two flavors to handle JSOP_*GVAR, which deals
365 * in slots not sprops, while not deoptimizing to map slot to sprop unless
366 * flags show this is necessary. The methodShapeChange overload (directly
367 * below) parallels this.
369 bool methodWriteBarrier(JSContext
*cx
, JSScopeProperty
*sprop
, const js::Value
&v
);
370 bool methodWriteBarrier(JSContext
*cx
, uint32 slot
, const js::Value
&v
);
372 void trace(JSTracer
*trc
);
374 void deletingShapeChange(JSContext
*cx
, JSScopeProperty
*sprop
);
375 bool methodShapeChange(JSContext
*cx
, JSScopeProperty
*sprop
);
376 bool methodShapeChange(JSContext
*cx
, uint32 slot
);
377 void protoShapeChange(JSContext
*cx
);
378 void shadowingShapeChange(JSContext
*cx
, JSScopeProperty
*sprop
);
379 bool globalObjectOwnShapeChange(JSContext
*cx
);
381 /* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */
382 #define SCOPE_CAPACITY(scope) JS_BIT(JS_DHASH_BITS-(scope)->hashShift)
385 DICTIONARY_MODE
= 0x0001,
388 INDEXED_PROPERTIES
= 0x0008,
390 METHOD_BARRIER
= 0x0020,
393 * This flag toggles with each shape-regenerating GC cycle.
394 * See JSRuntime::gcRegenShapesScopeFlag.
396 SHAPE_REGEN
= 0x0040,
398 /* The anti-branded flag, to avoid overspecializing. */
402 bool inDictionaryMode() { return flags
& DICTIONARY_MODE
; }
403 void setDictionaryMode() { flags
|= DICTIONARY_MODE
; }
404 void clearDictionaryMode() { flags
&= ~DICTIONARY_MODE
; }
407 * Don't define clearSealed, as it can't be done safely because JS_LOCK_OBJ
408 * will avoid taking the lock if the object owns its scope and the scope is
411 bool sealed() { return flags
& SEALED
; }
413 void seal(JSContext
*cx
) {
414 JS_ASSERT(!isSharedEmpty());
415 JS_ASSERT(!sealed());
416 generateOwnShape(cx
);
421 * A branded scope's object contains plain old methods (function-valued
422 * properties without magic getters and setters), and its scope->shape
423 * evolves whenever a function value changes.
425 bool branded() { return flags
& BRANDED
; }
427 bool brand(JSContext
*cx
, uint32 slot
, const js::Value
&) {
428 JS_ASSERT(!generic());
429 JS_ASSERT(!branded());
430 generateOwnShape(cx
);
431 if (js_IsPropertyCacheDisabled(cx
)) // check for rt->shapeGen overflow
437 bool generic() { return flags
& GENERIC
; }
440 * Here and elsewhere "unbrand" means "make generic". We never actually
441 * clear the BRANDED bit on any object. Once branded, there's no point in
442 * being generic, since the shape has already evolved unpredictably. So
443 * obj->unbrand() on a branded object does nothing.
445 void unbrand(JSContext
*cx
) {
450 bool hadIndexedProperties() { return flags
& INDEXED_PROPERTIES
; }
451 void setIndexedProperties() { flags
|= INDEXED_PROPERTIES
; }
453 bool hasOwnShape() { return flags
& OWN_SHAPE
; }
455 bool hasRegenFlag(uint8 regenFlag
) { return (flags
& SHAPE_REGEN
) == regenFlag
; }
458 * A scope has a method barrier when some compiler-created "null closure"
459 * function objects (functions that do not use lexical bindings above
460 * their scope, only free variable names) that have a correct parent value
461 * thanks to the COMPILE_N_GO optimization are stored as newly added direct
462 * property values of the scope's object.
464 * The de-facto standard JS language requires each evaluation of such a
465 * closure to result in a unique (according to === and observable effects)
466 * function object. ES3 tried to allow implementations to "join" such
467 * objects to a single compiler-created object, but this makes an overt
468 * mutation hazard, also an "identity hazard" against interoperation among
469 * implementations that join and do not join.
471 * To stay compatible with the de-facto standard, we store the compiler-
472 * created function object as the method value and set the METHOD_BARRIER
475 * The method value is part of the method property tree node's identity, so
476 * it effectively brands the scope with a predictable shape corresponding
477 * to the method value, but without the overhead of setting the BRANDED
478 * flag, which requires assigning a new shape peculiar to each branded
479 * scope. Instead the shape is shared via the property tree among all the
480 * scopes referencing the method property tree node.
482 * Then when reading from a scope for which scope->hasMethodBarrier() is
483 * true, we count on the scope's qualified/guarded shape being unique and
484 * add a read barrier that clones the compiler-created function object on
485 * demand, reshaping the scope.
487 * This read barrier is bypassed when evaluating the callee sub-expression
488 * of a call expression (see the JOF_CALLOP opcodes in jsopcode.tbl), since
489 * such ops do not present an identity or mutation hazard. The compiler
490 * performs this optimization only for null closures that do not use their
491 * own name or equivalent built-in references (arguments.callee).
493 * The BRANDED write barrier, JSScope::methodWriteBarrer, must check for
494 * METHOD_BARRIER too, and regenerate this scope's shape if the method's
495 * value is in fact changing.
497 bool hasMethodBarrier() { return flags
& METHOD_BARRIER
; }
498 void setMethodBarrier() { flags
|= METHOD_BARRIER
; }
501 * Test whether this scope may be branded due to method calls, which means
502 * any assignment to a function-valued property must regenerate shape; else
503 * test whether this scope has method properties, which require a method
507 brandedOrHasMethodBarrier() { return flags
& (BRANDED
| METHOD_BARRIER
); }
509 bool isSharedEmpty() const { return !object
; }
511 static bool initRuntimeState(JSContext
*cx
);
512 static void finishRuntimeState(JSContext
*cx
);
515 EMPTY_ARGUMENTS_SHAPE
= 1,
516 EMPTY_BLOCK_SHAPE
= 2,
517 EMPTY_CALL_SHAPE
= 3,
518 EMPTY_DECL_ENV_SHAPE
= 4,
519 EMPTY_ENUMERATOR_SHAPE
= 5,
520 EMPTY_WITH_SHAPE
= 6,
521 LAST_RESERVED_SHAPE
= 6
525 struct JSEmptyScope
: public JSScope
527 js::Class
* const clasp
;
528 jsrefcount nrefs
; /* count of all referencing objects */
530 JSEmptyScope(JSContext
*cx
, js::Class
*clasp
);
532 JSEmptyScope
*hold() {
533 /* The method is only called for already held objects. */
534 JS_ASSERT(nrefs
>= 1);
535 JS_ATOMIC_INCREMENT(&nrefs
);
539 void drop(JSContext
*cx
) {
540 JS_ASSERT(nrefs
>= 1);
541 JS_ATOMIC_DECREMENT(&nrefs
);
547 * Optimized version of the drop method to use from the object finalizer
548 * to skip expensive JS_ATOMIC_DECREMENT.
550 void dropFromGC(JSContext
*cx
) {
552 JS_ASSERT(CX_THREAD_IS_RUNNING_GC(cx
));
554 JS_ASSERT(nrefs
>= 1);
562 JS_IS_SCOPE_LOCKED(JSContext
*cx
, JSScope
*scope
)
564 return JS_IS_TITLE_LOCKED(cx
, &scope
->title
);
568 JSObject::scope() const
570 JS_ASSERT(isNative());
571 return (JSScope
*) map
;
575 JSObject::shape() const
577 JS_ASSERT(map
->shape
!= JSObjectMap::SHAPELESS
);
581 inline const js::Value
&
582 JSObject::lockedGetSlot(uintN slot
) const
584 OBJ_CHECK_SLOT(this, slot
);
585 return this->getSlot(slot
);
589 JSObject::lockedSetSlot(uintN slot
, const js::Value
&value
)
591 OBJ_CHECK_SLOT(this, slot
);
592 this->setSlot(slot
, value
);
601 struct JSScopeProperty
{
602 friend struct JSScope
;
603 friend class js::PropertyTree
;
604 friend JSDHashOperator
js::RemoveNodeIfDead(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
605 uint32 number
, void *arg
);
606 friend void js::SweepScopeProperties(JSContext
*cx
);
612 js::PropertyOp rawGetter
; /* getter and setter hooks or objects */
613 JSObject
*getterObj
; /* user-defined callable "get" object or
614 null if sprop->hasGetterValue(); or
615 joined function object if METHOD flag
617 JSScopeProperty
*next
; /* next node in freelist */
621 js::PropertyOp rawSetter
; /* getter is JSObject* and setter is 0
622 if sprop->isMethod() */
623 JSObject
*setterObj
; /* user-defined callable "set" object or
624 null if sprop->hasSetterValue() */
625 JSScopeProperty
**prevp
; /* pointer to previous node's next, or
626 pointer to head of freelist */
629 void insertFree(JSScopeProperty
*&list
) {
639 JS_ASSERT(JSID_IS_VOID(id
));
646 uint32 slot
; /* abstract index in object slots */
648 uint8 attrs
; /* attributes, see jsapi.h JSPROP_* */
649 uint8 flags
; /* flags, see below for defines */
651 int16 shortid
; /* tinyid, or local arg/var index */
652 JSScopeProperty
*parent
; /* parent node, reverse for..in order */
654 JSScopeProperty
*kids
; /* null, single child, or a tagged ptr
655 to many-kids data structure */
656 JSScopeProperty
**childp
; /* dictionary list starting at lastProp
657 has a double-indirect back pointer,
658 either to sprop->parent if not last,
659 else to scope->lastProp */
661 uint32 shape
; /* property cache shape identifier */
665 * Implementation-private bits stored in sprop->flags. See public: enum {}
666 * flags further below, which were allocated FCFS over time, so interleave
674 * Set during a shape-regenerating GC if the shape has already been
675 * regenerated. Unlike JSScope::SHAPE_REGEN, this does not toggle with
676 * each GC. js::SweepScopeProperties clears it.
680 /* Property stored in per-object dictionary, not shared property tree. */
684 JSScopeProperty(jsid id
, js::PropertyOp getter
, js::PropertyOp setter
, uint32 slot
,
685 uintN attrs
, uintN flags
, intN shortid
);
687 bool marked() const { return (flags
& MARK
) != 0; }
688 void mark() { flags
|= MARK
; }
689 void clearMark() { flags
&= ~MARK
; }
691 bool hasRegenFlag() const { return (flags
& SHAPE_REGEN
) != 0; }
692 void setRegenFlag() { flags
|= SHAPE_REGEN
; }
693 void clearRegenFlag() { flags
&= ~SHAPE_REGEN
; }
695 bool inDictionary() const { return (flags
& IN_DICTIONARY
) != 0; }
698 /* Public bits stored in sprop->flags. */
703 PUBLIC_FLAGS
= ALIAS
| HAS_SHORTID
| METHOD
706 uintN
getFlags() const { return flags
& PUBLIC_FLAGS
; }
707 bool isAlias() const { return (flags
& ALIAS
) != 0; }
708 bool hasShortID() const { return (flags
& HAS_SHORTID
) != 0; }
709 bool isMethod() const { return (flags
& METHOD
) != 0; }
711 JSObject
&methodObject() const { JS_ASSERT(isMethod()); return *getterObj
; }
713 js::PropertyOp
getter() const { return rawGetter
; }
714 bool hasDefaultGetter() const { return !rawGetter
; }
715 js::PropertyOp
getterOp() const { JS_ASSERT(!hasGetterValue()); return rawGetter
; }
716 JSObject
*getterObject() const { JS_ASSERT(hasGetterValue()); return getterObj
; }
718 // Per ES5, decode null getterObj as the undefined value, which encodes as null.
719 js::Value
getterValue() const {
720 JS_ASSERT(hasGetterValue());
721 return getterObj
? js::ObjectValue(*getterObj
) : js::UndefinedValue();
724 js::Value
getterOrUndefined() const {
725 return hasGetterValue() && getterObj
? js::ObjectValue(*getterObj
) : js::UndefinedValue();
728 js::PropertyOp
setter() const { return rawSetter
; }
729 bool hasDefaultSetter() const { return !rawSetter
; }
730 js::PropertyOp
setterOp() const { JS_ASSERT(!hasSetterValue()); return rawSetter
; }
731 JSObject
*setterObject() const { JS_ASSERT(hasSetterValue()); return setterObj
; }
733 // Per ES5, decode null setterObj as the undefined value, which encodes as null.
734 js::Value
setterValue() const {
735 JS_ASSERT(hasSetterValue());
736 return setterObj
? js::ObjectValue(*setterObj
) : js::UndefinedValue();
739 js::Value
setterOrUndefined() const {
740 return hasSetterValue() && setterObj
? js::ObjectValue(*setterObj
) : js::UndefinedValue();
743 inline JSDHashNumber
hash() const;
744 inline bool matches(const JSScopeProperty
*p
) const;
745 inline bool matchesParamsAfterId(js::PropertyOp agetter
, js::PropertyOp asetter
,
746 uint32 aslot
, uintN aattrs
, uintN aflags
,
747 intN ashortid
) const;
749 bool get(JSContext
* cx
, JSObject
*obj
, JSObject
*pobj
, js::Value
* vp
);
750 bool set(JSContext
* cx
, JSObject
*obj
, js::Value
* vp
);
752 inline bool isSharedPermanent() const;
754 void trace(JSTracer
*trc
);
756 bool hasSlot() const { return (attrs
& JSPROP_SHARED
) == 0; }
758 uint8
attributes() const { return attrs
; }
759 bool configurable() const { return (attrs
& JSPROP_PERMANENT
) == 0; }
760 bool enumerable() const { return (attrs
& JSPROP_ENUMERATE
) != 0; }
761 bool writable() const {
762 // JS_ASSERT(isDataDescriptor());
763 return (attrs
& JSPROP_READONLY
) == 0;
765 bool hasGetterValue() const { return attrs
& JSPROP_GETTER
; }
766 bool hasSetterValue() const { return attrs
& JSPROP_SETTER
; }
768 bool hasDefaultGetterOrIsMethod() const {
769 return hasDefaultGetter() || isMethod();
772 bool isDataDescriptor() const {
773 return (attrs
& (JSPROP_SETTER
| JSPROP_GETTER
)) == 0;
775 bool isAccessorDescriptor() const {
776 return (attrs
& (JSPROP_SETTER
| JSPROP_GETTER
)) != 0;
780 void dump(JSContext
*cx
, FILE *fp
);
781 void dumpSubtree(JSContext
*cx
, int level
, FILE *fp
);
785 /* JSScopeProperty pointer tag bit indicating a collision. */
786 #define SPROP_COLLISION ((jsuword)1)
787 #define SPROP_REMOVED ((JSScopeProperty *) SPROP_COLLISION)
789 /* Macros to get and set sprop pointer values and collision flags. */
790 #define SPROP_IS_FREE(sprop) ((sprop) == NULL)
791 #define SPROP_IS_REMOVED(sprop) ((sprop) == SPROP_REMOVED)
792 #define SPROP_IS_LIVE(sprop) ((sprop) > SPROP_REMOVED)
793 #define SPROP_FLAG_COLLISION(spp,sprop) (*(spp) = (JSScopeProperty *) \
794 ((jsuword)(sprop) | SPROP_COLLISION))
795 #define SPROP_HAD_COLLISION(sprop) ((jsuword)(sprop) & SPROP_COLLISION)
796 #define SPROP_FETCH(spp) SPROP_CLEAR_COLLISION(*(spp))
798 #define SPROP_CLEAR_COLLISION(sprop) \
799 ((JSScopeProperty *) ((jsuword)(sprop) & ~SPROP_COLLISION))
801 #define SPROP_STORE_PRESERVING_COLLISION(spp, sprop) \
802 (*(spp) = (JSScopeProperty *) ((jsuword)(sprop) \
803 | SPROP_HAD_COLLISION(*(spp))))
805 inline JSScopeProperty
*
806 JSScope::lookup(jsid id
)
808 return SPROP_FETCH(search(id
, false));
812 JSScope::hasProperty(JSScopeProperty
*sprop
)
814 return lookup(sprop
->id
) == sprop
;
817 inline JSScopeProperty
*
818 JSScope::lastProperty() const
820 JS_ASSERT_IF(lastProp
, !JSID_IS_VOID(lastProp
->id
));
825 * Note that sprop must not be null, as emptying a scope requires extra work
826 * done only by methods in jsscope.cpp.
829 JSScope::setLastProperty(JSScopeProperty
*sprop
)
831 JS_ASSERT(!JSID_IS_VOID(sprop
->id
));
832 JS_ASSERT_IF(lastProp
, !JSID_IS_VOID(lastProp
->id
));
838 JSScope::removeLastProperty()
840 JS_ASSERT(!inDictionaryMode());
841 JS_ASSERT_IF(lastProp
->parent
, !JSID_IS_VOID(lastProp
->parent
->id
));
843 lastProp
= lastProp
->parent
;
848 JSScope::removeDictionaryProperty(JSScopeProperty
*sprop
)
850 JS_ASSERT(inDictionaryMode());
851 JS_ASSERT(sprop
->inDictionary());
852 JS_ASSERT(sprop
->childp
);
853 JS_ASSERT(!JSID_IS_VOID(sprop
->id
));
855 JS_ASSERT(lastProp
->inDictionary());
856 JS_ASSERT(lastProp
->childp
== &lastProp
);
857 JS_ASSERT_IF(lastProp
!= sprop
, !JSID_IS_VOID(lastProp
->id
));
858 JS_ASSERT_IF(lastProp
->parent
, !JSID_IS_VOID(lastProp
->parent
->id
));
861 sprop
->parent
->childp
= sprop
->childp
;
862 *sprop
->childp
= sprop
->parent
;
864 sprop
->childp
= NULL
;
868 JSScope::insertDictionaryProperty(JSScopeProperty
*sprop
, JSScopeProperty
**childp
)
871 * Don't assert inDictionaryMode() here because we may be called from
872 * toDictionaryMode via newDictionaryProperty.
874 JS_ASSERT(sprop
->inDictionary());
875 JS_ASSERT(!sprop
->childp
);
876 JS_ASSERT(!JSID_IS_VOID(sprop
->id
));
878 JS_ASSERT_IF(*childp
, (*childp
)->inDictionary());
879 JS_ASSERT_IF(lastProp
, lastProp
->inDictionary());
880 JS_ASSERT_IF(lastProp
, lastProp
->childp
== &lastProp
);
881 JS_ASSERT_IF(lastProp
, !JSID_IS_VOID(lastProp
->id
));
883 sprop
->parent
= *childp
;
886 sprop
->parent
->childp
= &sprop
->parent
;
887 sprop
->childp
= childp
;
892 * If SHORTID is set in sprop->flags, we use sprop->shortid rather
893 * than id when calling sprop's getter or setter.
895 #define SPROP_USERID(sprop) \
896 ((sprop)->hasShortID() ? INT_TO_JSID((sprop)->shortid) \
899 #define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->freeslot)
900 #define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope)
902 #ifndef JS_THREADSAFE
903 # define js_GenerateShape(cx, gcLocked) js_GenerateShape (cx)
907 js_GenerateShape(JSContext
*cx
, bool gcLocked
);
910 struct JSScopeStats
{
917 jsrefcount stepMisses
;
918 jsrefcount tableAllocFails
;
919 jsrefcount toDictFails
;
920 jsrefcount wrapWatchFails
;
924 jsrefcount redundantPuts
;
927 jsrefcount changeFails
;
928 jsrefcount compresses
;
931 jsrefcount removeFrees
;
932 jsrefcount uselessRemoves
;
936 extern JS_FRIEND_DATA(JSScopeStats
) js_scope_stats
;
938 # define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x)
940 # define METER(x) /* nothing */
943 inline JSScopeProperty
**
944 JSScope::search(jsid id
, bool adding
)
946 JSScopeProperty
*sprop
, **spp
;
950 /* Not enough properties to justify hashing: search from lastProp. */
951 for (spp
= &lastProp
; (sprop
= *spp
); spp
= &sprop
->parent
) {
952 if (sprop
->id
== id
) {
960 return searchTable(id
, adding
);
966 JSScope::canProvideEmptyScope(js::Class
*clasp
)
969 * An empty scope cannot provide another empty scope, or wrongful two-level
970 * prototype shape sharing ensues -- see bug 497789.
974 return !emptyScope
|| emptyScope
->clasp
== clasp
;
978 JSScopeProperty::isSharedPermanent() const
980 return (~attrs
& (JSPROP_SHARED
| JSPROP_PERMANENT
)) == 0;
984 js_GetMutableScope(JSContext
*cx
, JSObject
*obj
);
988 class AutoObjectLocker
{
989 JSContext
* const cx
;
990 JSObject
* const obj
;
992 AutoObjectLocker(JSContext
*cx
, JSObject
*obj
)
994 JS_LOCK_OBJ(cx
, obj
);
997 ~AutoObjectLocker() { JS_UNLOCK_OBJ(cx
, obj
); }
1003 #pragma warning(pop)
1004 #pragma warning(pop)
1007 #endif /* jsscope_h___ */