Bug 586358: make imacpc flagged. (r=lw)
[mozilla-central.git] / js / src / jsscope.h
blob47ebe52a78afd3d8f535a0a03bd8fb2fd970c3d0
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
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 ***** */
41 #ifndef jsscope_h___
42 #define jsscope_h___
44 * JS symbol tables.
46 #ifdef DEBUG
47 #include <stdio.h>
48 #endif
50 #include "jstypes.h"
51 #include "jscntxt.h"
52 #include "jslock.h"
53 #include "jsobj.h"
54 #include "jsprvtd.h"
55 #include "jspubtd.h"
56 #include "jspropertycache.h"
57 #include "jspropertytree.h"
59 #ifdef _MSC_VER
60 #pragma warning(push)
61 #pragma warning(disable:4800)
62 #pragma warning(push)
63 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
64 #endif
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
72 * nodes, is worth it.
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
93 * links to its kids.)
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
147 * by double hashing.
149 * Therefore,
151 * (property tree) <> (no property tree)
152 * N*(S+L) + T <> S*T
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
172 * beta < 5/(8*alpha)
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.
214 struct JSEmptyScope;
216 #define SPROP_INVALID_SLOT 0xffffffff
218 struct JSScope : public JSObjectMap
220 #ifdef JS_THREADSAFE
221 JSTitle title; /* lock state */
222 #endif
223 JSObject *object; /* object that owns this scope */
224 uint32 freeslot; /* index of next free slot in object */
225 protected:
226 uint8 flags; /* flags, see below */
227 public:
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;
242 private:
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);
274 protected:
275 void initMinimal(JSContext *cx, uint32 newShape);
277 private:
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);
296 public:
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)
384 enum {
385 DICTIONARY_MODE = 0x0001,
386 SEALED = 0x0002,
387 BRANDED = 0x0004,
388 INDEXED_PROPERTIES = 0x0008,
389 OWN_SHAPE = 0x0010,
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. */
399 GENERIC = 0x0080
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
409 * sealed.
411 bool sealed() { return flags & SEALED; }
413 void seal(JSContext *cx) {
414 JS_ASSERT(!isSharedEmpty());
415 JS_ASSERT(!sealed());
416 generateOwnShape(cx);
417 flags |= SEALED;
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
432 return false;
433 flags |= BRANDED;
434 return true;
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) {
446 if (!branded())
447 flags |= GENERIC;
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
473 * flag.
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
504 * write barrier.
506 bool
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);
514 enum {
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);
536 return this;
539 void drop(JSContext *cx) {
540 JS_ASSERT(nrefs >= 1);
541 JS_ATOMIC_DECREMENT(&nrefs);
542 if (nrefs == 0)
543 destroy(cx);
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) {
551 #ifdef JS_THREADSAFE
552 JS_ASSERT(CX_THREAD_IS_RUNNING_GC(cx));
553 #endif
554 JS_ASSERT(nrefs >= 1);
555 --nrefs;
556 if (nrefs == 0)
557 destroy(cx);
561 inline bool
562 JS_IS_SCOPE_LOCKED(JSContext *cx, JSScope *scope)
564 return JS_IS_TITLE_LOCKED(cx, &scope->title);
567 inline JSScope *
568 JSObject::scope() const
570 JS_ASSERT(isNative());
571 return (JSScope *) map;
574 inline uint32
575 JSObject::shape() const
577 JS_ASSERT(map->shape != JSObjectMap::SHAPELESS);
578 return map->shape;
581 inline const js::Value &
582 JSObject::lockedGetSlot(uintN slot) const
584 OBJ_CHECK_SLOT(this, slot);
585 return this->getSlot(slot);
588 inline void
589 JSObject::lockedSetSlot(uintN slot, const js::Value &value)
591 OBJ_CHECK_SLOT(this, slot);
592 this->setSlot(slot, value);
595 namespace js {
597 class PropertyTree;
599 } /* namespace js */
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);
608 jsid id;
610 private:
611 union {
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
616 is set. */
617 JSScopeProperty *next; /* next node in freelist */
620 union {
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) {
630 id = JSID_VOID;
631 next = list;
632 prevp = &list;
633 if (list)
634 list->prevp = &next;
635 list = this;
638 void removeFree() {
639 JS_ASSERT(JSID_IS_VOID(id));
640 *prevp = next;
641 if (next)
642 next->prevp = prevp;
645 public:
646 uint32 slot; /* abstract index in object slots */
647 private:
648 uint8 attrs; /* attributes, see jsapi.h JSPROP_* */
649 uint8 flags; /* flags, see below for defines */
650 public:
651 int16 shortid; /* tinyid, or local arg/var index */
652 JSScopeProperty *parent; /* parent node, reverse for..in order */
653 union {
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 */
663 private:
665 * Implementation-private bits stored in sprop->flags. See public: enum {}
666 * flags further below, which were allocated FCFS over time, so interleave
667 * with these bits.
669 enum {
670 /* GC mark flag. */
671 MARK = 0x01,
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.
678 SHAPE_REGEN = 0x08,
680 /* Property stored in per-object dictionary, not shared property tree. */
681 IN_DICTIONARY = 0x20
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; }
697 public:
698 /* Public bits stored in sprop->flags. */
699 enum {
700 ALIAS = 0x02,
701 HAS_SHORTID = 0x04,
702 METHOD = 0x10,
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;
779 #ifdef DEBUG
780 void dump(JSContext *cx, FILE *fp);
781 void dumpSubtree(JSContext *cx, int level, FILE *fp);
782 #endif
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));
811 inline bool
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));
821 return lastProp;
825 * Note that sprop must not be null, as emptying a scope requires extra work
826 * done only by methods in jsscope.cpp.
828 inline void
829 JSScope::setLastProperty(JSScopeProperty *sprop)
831 JS_ASSERT(!JSID_IS_VOID(sprop->id));
832 JS_ASSERT_IF(lastProp, !JSID_IS_VOID(lastProp->id));
834 lastProp = sprop;
837 inline void
838 JSScope::removeLastProperty()
840 JS_ASSERT(!inDictionaryMode());
841 JS_ASSERT_IF(lastProp->parent, !JSID_IS_VOID(lastProp->parent->id));
843 lastProp = lastProp->parent;
844 --entryCount;
847 inline void
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));
860 if (sprop->parent)
861 sprop->parent->childp = sprop->childp;
862 *sprop->childp = sprop->parent;
863 --entryCount;
864 sprop->childp = NULL;
867 inline void
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;
884 *childp = sprop;
885 if (sprop->parent)
886 sprop->parent->childp = &sprop->parent;
887 sprop->childp = childp;
888 ++entryCount;
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) \
897 : (sprop)->id)
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)
904 #endif
906 extern uint32
907 js_GenerateShape(JSContext *cx, bool gcLocked);
909 #ifdef DEBUG
910 struct JSScopeStats {
911 jsrefcount searches;
912 jsrefcount hits;
913 jsrefcount misses;
914 jsrefcount hashes;
915 jsrefcount steps;
916 jsrefcount stepHits;
917 jsrefcount stepMisses;
918 jsrefcount tableAllocFails;
919 jsrefcount toDictFails;
920 jsrefcount wrapWatchFails;
921 jsrefcount adds;
922 jsrefcount addFails;
923 jsrefcount puts;
924 jsrefcount redundantPuts;
925 jsrefcount putFails;
926 jsrefcount changes;
927 jsrefcount changeFails;
928 jsrefcount compresses;
929 jsrefcount grows;
930 jsrefcount removes;
931 jsrefcount removeFrees;
932 jsrefcount uselessRemoves;
933 jsrefcount shrinks;
936 extern JS_FRIEND_DATA(JSScopeStats) js_scope_stats;
938 # define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x)
939 #else
940 # define METER(x) /* nothing */
941 #endif
943 inline JSScopeProperty **
944 JSScope::search(jsid id, bool adding)
946 JSScopeProperty *sprop, **spp;
948 METER(searches);
949 if (!table) {
950 /* Not enough properties to justify hashing: search from lastProp. */
951 for (spp = &lastProp; (sprop = *spp); spp = &sprop->parent) {
952 if (sprop->id == id) {
953 METER(hits);
954 return spp;
957 METER(misses);
958 return spp;
960 return searchTable(id, adding);
963 #undef METER
965 inline bool
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.
972 if (!object)
973 return false;
974 return !emptyScope || emptyScope->clasp == clasp;
977 inline bool
978 JSScopeProperty::isSharedPermanent() const
980 return (~attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0;
983 extern JSScope *
984 js_GetMutableScope(JSContext *cx, JSObject *obj);
986 namespace js {
988 class AutoObjectLocker {
989 JSContext * const cx;
990 JSObject * const obj;
991 public:
992 AutoObjectLocker(JSContext *cx, JSObject *obj)
993 : cx(cx), obj(obj) {
994 JS_LOCK_OBJ(cx, obj);
997 ~AutoObjectLocker() { JS_UNLOCK_OBJ(cx, obj); }
1002 #ifdef _MSC_VER
1003 #pragma warning(pop)
1004 #pragma warning(pop)
1005 #endif
1007 #endif /* jsscope_h___ */