Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / js / src / jsscope.h
blob99aefa13a33673ecb4bd66fe8fad29638fa39419
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 #include <new>
47 #ifdef DEBUG
48 #include <stdio.h>
49 #endif
51 #include "jstypes.h"
52 #include "jscntxt.h"
53 #include "jscompartment.h"
54 #include "jshashtable.h"
55 #include "jsobj.h"
56 #include "jsprvtd.h"
57 #include "jspubtd.h"
58 #include "jspropertytree.h"
59 #include "jsstrinlines.h"
61 #ifdef _MSC_VER
62 #pragma warning(push)
63 #pragma warning(disable:4800)
64 #pragma warning(push)
65 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
66 #endif
69 * Given P independent, non-unique properties each of size S words mapped by
70 * all scopes in a runtime, construct a property tree of N nodes each of size
71 * S+L words (L for tree linkage). A nominal L value is 2 for leftmost-child
72 * and right-sibling links. We hope that the N < P by enough that the space
73 * overhead of L, and the overhead of scope entries pointing at property tree
74 * nodes, is worth it.
76 * The tree construction goes as follows. If any empty scope in the runtime
77 * has a property X added to it, find or create a node under the tree root
78 * labeled X, and set obj->lastProp to point at that node. If any non-empty
79 * scope whose most recently added property is labeled Y has another property
80 * labeled Z added, find or create a node for Z under the node that was added
81 * for Y, and set obj->lastProp to point at that node.
83 * A property is labeled by its members' values: id, getter, setter, slot,
84 * attributes, tiny or short id, and a field telling for..in order. Note that
85 * labels are not unique in the tree, but they are unique among a node's kids
86 * (barring rare and benign multi-threaded race condition outcomes, see below)
87 * and along any ancestor line from the tree root to a given leaf node (except
88 * for the hard case of duplicate formal parameters to a function).
90 * Thus the root of the tree represents all empty scopes, and the first ply
91 * of the tree represents all scopes containing one property, etc. Each node
92 * in the tree can stand for any number of scopes having the same ordered set
93 * of properties, where that node was the last added to the scope. (We need
94 * not store the root of the tree as a node, and do not -- all we need are
95 * links to its kids.)
97 * Sidebar on for..in loop order: ECMA requires no particular order, but this
98 * implementation has promised and delivered property definition order, and
99 * compatibility is king. We could use an order number per property, which
100 * would require a sort in js_Enumerate, and an entry order generation number
101 * per scope. An order number beats a list, which should be doubly-linked for
102 * O(1) delete. An even better scheme is to use a parent link in the property
103 * tree, so that the ancestor line can be iterated from obj->lastProp when
104 * filling in a JSIdArray from back to front. This parent link also helps the
105 * GC to sweep properties iteratively.
107 * What if a property Y is deleted from a scope? If Y is the last property in
108 * the scope, we simply adjust the scope's lastProp member after we remove the
109 * scope's hash-table entry pointing at that property node. The parent link
110 * mentioned in the for..in sidebar above makes this adjustment O(1). But if
111 * Y comes between X and Z in the scope, then we might have to "fork" the tree
112 * at X, leaving X->Y->Z in case other scopes have those properties added in
113 * that order; and to finish the fork, we'd add a node labeled Z with the path
114 * X->Z, if it doesn't exist. This could lead to lots of extra nodes, and to
115 * O(n^2) growth when deleting lots of properties.
117 * Rather, for O(1) growth all around, we should share the path X->Y->Z among
118 * scopes having those three properties added in that order, and among scopes
119 * having only X->Z where Y was deleted. All such scopes have a lastProp that
120 * points to the Z child of Y. But a scope in which Y was deleted does not
121 * have a table entry for Y, and when iterating that scope by traversing the
122 * ancestor line from Z, we will have to test for a table entry for each node,
123 * skipping nodes that lack entries.
125 * What if we add Y again? X->Y->Z->Y is wrong and we'll enumerate Y twice.
126 * Therefore we must fork in such a case if not earlier, or do something else.
127 * We used to fork on the theory that set after delete is rare, but the Web is
128 * a harsh mistress, and we now convert the scope to a "dictionary" on first
129 * delete, to avoid O(n^2) growth in the property tree.
131 * What about thread safety? If the property tree operations done by requests
132 * are find-node and insert-node, then the only hazard is duplicate insertion.
133 * This is harmless except for minor bloat. When all requests have ended or
134 * been suspended, the GC is free to sweep the tree after marking all nodes
135 * reachable from scopes, performing remove-node operations as needed.
137 * Is the property tree worth it compared to property storage in each table's
138 * entries? To decide, we must find the relation <> between the words used
139 * with a property tree and the words required without a tree.
141 * Model all scopes as one super-scope of capacity T entries (T a power of 2).
142 * Let alpha be the load factor of this double hash-table. With the property
143 * tree, each entry in the table is a word-sized pointer to a node that can be
144 * shared by many scopes. But all such pointers are overhead compared to the
145 * situation without the property tree, where the table stores property nodes
146 * directly, as entries each of size S words. With the property tree, we need
147 * L=2 extra words per node for siblings and kids pointers. Without the tree,
148 * (1-alpha)*S*T words are wasted on free or removed sentinel-entries required
149 * by double hashing.
151 * Therefore,
153 * (property tree) <> (no property tree)
154 * N*(S+L) + T <> S*T
155 * N*(S+L) + T <> P*S + (1-alpha)*S*T
156 * N*(S+L) + alpha*T + (1-alpha)*T <> P*S + (1-alpha)*S*T
158 * Note that P is alpha*T by definition, so
160 * N*(S+L) + P + (1-alpha)*T <> P*S + (1-alpha)*S*T
161 * N*(S+L) <> P*S - P + (1-alpha)*S*T - (1-alpha)*T
162 * N*(S+L) <> (P + (1-alpha)*T) * (S-1)
163 * N*(S+L) <> (P + (1-alpha)*P/alpha) * (S-1)
164 * N*(S+L) <> P * (1/alpha) * (S-1)
166 * Let N = P*beta for a compression ratio beta, beta <= 1:
168 * P*beta*(S+L) <> P * (1/alpha) * (S-1)
169 * beta*(S+L) <> (S-1)/alpha
170 * beta <> (S-1)/((S+L)*alpha)
172 * For S = 6 (32-bit architectures) and L = 2, the property tree wins iff
174 * beta < 5/(8*alpha)
176 * We ensure that alpha <= .75, so the property tree wins if beta < .83_. An
177 * average beta from recent Mozilla browser startups was around .6.
179 * Can we reduce L? Observe that the property tree degenerates into a list of
180 * lists if at most one property Y follows X in all scopes. In or near such a
181 * case, we waste a word on the right-sibling link outside of the root ply of
182 * the tree. Note also that the root ply tends to be large, so O(n^2) growth
183 * searching it is likely, indicating the need for hashing (but with increased
184 * thread safety costs).
186 * If only K out of N nodes in the property tree have more than one child, we
187 * could eliminate the sibling link and overlay a children list or hash-table
188 * pointer on the leftmost-child link (which would then be either null or an
189 * only-child link; the overlay could be tagged in the low bit of the pointer,
190 * or flagged elsewhere in the property tree node, although such a flag must
191 * not be considered when comparing node labels during tree search).
193 * For such a system, L = 1 + (K * averageChildrenTableSize) / N instead of 2.
194 * If K << N, L approaches 1 and the property tree wins if beta < .95.
196 * We observe that fan-out below the root ply of the property tree appears to
197 * have extremely low degree (see the MeterPropertyTree code that histograms
198 * child-counts in jsscope.c), so instead of a hash-table we use a linked list
199 * of child node pointer arrays ("kid chunks"). The details are isolated in
200 * jspropertytree.h/.cpp; others must treat js::Shape.kids as opaque.
202 * One final twist (can you stand it?): the vast majority (~95% or more) of
203 * scopes are looked up fewer than three times; in these cases, initializing
204 * scope->table isn't worth it. So instead of always allocating scope->table,
205 * we leave it null while initializing all the other scope members as if it
206 * were non-null and minimal-length. Until a scope is searched
207 * MAX_LINEAR_SEARCHES times, we use linear search from obj->lastProp to find a
208 * given id, and save on the time and space overhead of creating a hash table.
211 #define SHAPE_INVALID_SLOT 0xffffffff
213 namespace js {
216 * Shapes use multiplicative hashing, _a la_ jsdhash.[ch], but specialized to
217 * minimize footprint. But if a Shape lineage has been searched fewer than
218 * MAX_LINEAR_SEARCHES times, we use linear search and avoid allocating
219 * scope->table.
221 struct PropertyTable {
222 static const uint32 MAX_LINEAR_SEARCHES = 7;
223 static const uint32 MIN_SIZE_LOG2 = 4;
224 static const uint32 MIN_SIZE = JS_BIT(MIN_SIZE_LOG2);
226 int hashShift; /* multiplicative hash shift */
228 uint32 entryCount; /* number of entries in table */
229 uint32 removedCount; /* removed entry sentinels in table */
230 uint32 freelist; /* SHAPE_INVALID_SLOT or head of slot
231 freelist in owning dictionary-mode
232 object */
233 js::Shape **entries; /* table of ptrs to shared tree nodes */
235 PropertyTable(uint32 nentries)
236 : hashShift(JS_DHASH_BITS - MIN_SIZE_LOG2),
237 entryCount(nentries),
238 removedCount(0),
239 freelist(SHAPE_INVALID_SLOT)
241 /* NB: entries is set by init, which must be called. */
244 ~PropertyTable() {
245 js_free(entries);
248 /* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */
249 uint32 capacity() const { return JS_BIT(JS_DHASH_BITS - hashShift); }
251 /* Whether we need to grow. We want to do this if the load factor is >= 0.75 */
252 bool needsToGrow() const {
253 uint32 size = capacity();
254 return entryCount + removedCount >= size - (size >> 2);
258 * Try to grow the table. On failure, reports out of memory on cx
259 * and returns false. This will make any extant pointers into the
260 * table invalid. Don't call this unless needsToGrow() is true.
262 bool grow(JSContext *cx);
265 * NB: init and change are fallible but do not report OOM, so callers can
266 * cope or ignore. They do however use JSRuntime's calloc method in order
267 * to update the malloc counter on success.
269 bool init(JSRuntime *rt, js::Shape *lastProp);
270 bool change(int log2Delta, JSContext *cx);
271 js::Shape **search(jsid id, bool adding);
274 } /* namespace js */
276 struct JSObject;
278 namespace js {
280 class PropertyTree;
282 static inline PropertyOp
283 CastAsPropertyOp(js::Class *clasp)
285 return JS_DATA_TO_FUNC_PTR(PropertyOp, clasp);
289 * Reuse the API-only JSPROP_INDEX attribute to mean shadowability.
291 #define JSPROP_SHADOWABLE JSPROP_INDEX
293 struct Shape : public JSObjectMap
295 friend struct ::JSObject;
296 friend struct ::JSFunction;
297 friend class js::PropertyTree;
298 friend class js::Bindings;
299 friend bool IsShapeAboutToBeFinalized(JSContext *cx, const js::Shape *shape);
302 * numLinearSearches starts at zero and is incremented initially on each
303 * search() call. Once numLinearSearches reaches MAX_LINEAR_SEARCHES
304 * (which is a small integer), the table is created on the next search()
305 * call, and the table pointer will be easily distinguishable from a small
306 * integer. The table can also be created when hashifying for dictionary
307 * mode.
309 union {
310 mutable size_t numLinearSearches;
311 mutable js::PropertyTable *table;
314 public:
315 inline void freeTable(JSContext *cx);
317 static bool initEmptyShapes(JSCompartment *comp);
318 static void finishEmptyShapes(JSCompartment *comp);
320 jsid id;
322 #ifdef DEBUG
323 JSCompartment *compartment;
324 #endif
326 protected:
327 union {
328 js::PropertyOp rawGetter; /* getter and setter hooks or objects */
329 JSObject *getterObj; /* user-defined callable "get" object or
330 null if shape->hasGetterValue(); or
331 joined function object if METHOD flag
332 is set. */
333 js::Class *clasp; /* prototype class for empty scope */
336 union {
337 js::StrictPropertyOp rawSetter;/* getter is JSObject* and setter is 0
338 if shape->isMethod() */
339 JSObject *setterObj; /* user-defined callable "set" object or
340 null if shape->hasSetterValue() */
343 public:
344 uint32 slot; /* abstract index in object slots */
345 private:
346 uint8 attrs; /* attributes, see jsapi.h JSPROP_* */
347 mutable uint8 flags; /* flags, see below for defines */
348 public:
349 int16 shortid; /* tinyid, or local arg/var index */
351 protected:
352 mutable js::Shape *parent; /* parent node, reverse for..in order */
353 union {
354 mutable js::KidsPointer kids; /* null, single child, or a tagged ptr
355 to many-kids data structure */
356 mutable js::Shape **listp; /* dictionary list starting at lastProp
357 has a double-indirect back pointer,
358 either to shape->parent if not last,
359 else to obj->lastProp */
362 static inline js::Shape **search(JSRuntime *rt, js::Shape **startp, jsid id,
363 bool adding = false);
364 static js::Shape *newDictionaryShape(JSContext *cx, const js::Shape &child, js::Shape **listp);
365 static js::Shape *newDictionaryList(JSContext *cx, js::Shape **listp);
367 inline void removeFromDictionary(JSObject *obj) const;
368 inline void insertIntoDictionary(js::Shape **dictp);
370 js::Shape *getChild(JSContext *cx, const js::Shape &child, js::Shape **listp);
372 bool hashify(JSRuntime *rt);
374 bool hasTable() const {
375 /* A valid pointer should be much bigger than MAX_LINEAR_SEARCHES. */
376 return numLinearSearches > PropertyTable::MAX_LINEAR_SEARCHES;
379 js::PropertyTable *getTable() const {
380 JS_ASSERT(hasTable());
381 return table;
384 void setTable(js::PropertyTable *t) const {
385 JS_ASSERT_IF(t && t->freelist != SHAPE_INVALID_SLOT, t->freelist < slotSpan);
386 table = t;
390 * Setter for parent. The challenge is to maintain JSObjectMap::slotSpan in
391 * the face of arbitrary slot order.
393 * By induction, an empty shape has a slotSpan member correctly computed as
394 * JSCLASS_FREE(clasp) -- see EmptyShape's constructor in jsscopeinlines.h.
395 * This is the basis case, where p is null.
397 * Any child shape, whether in a shape tree or in a dictionary list, must
398 * have a slotSpan either one greater than its slot value (if the child's
399 * slot is SHAPE_INVALID_SLOT, this will yield 0; the static assertion
400 * below enforces this), or equal to its parent p's slotSpan, whichever is
401 * greater. This is the inductive step.
403 * If we maintained shape paths such that parent slot was always one less
404 * than child slot, possibly with an exception for SHAPE_INVALID_SLOT slot
405 * values where we would use another way of computing slotSpan based on the
406 * PropertyTable (as JSC does), then we would not need to store slotSpan in
407 * Shape (to be precise, in its base struct, JSobjectMap).
409 * But we currently scramble slots along shape paths due to resolve-based
410 * creation of shapes mapping reserved slots, and we do not have the needed
411 * PropertyTable machinery to use as an alternative when parent slot is not
412 * one less than child slot. This machinery is neither simple nor free, as
413 * it must involve creating a table for any slot-less transition and then
414 * pinning the table to its shape.
416 * Use of 'delete' can scramble slots along the shape lineage too, although
417 * it always switches the target object to dictionary mode, so the cost of
418 * a pinned table is less onerous.
420 * Note that allocating a uint32 slotSpan member in JSObjectMap takes no
421 * net extra space on 64-bit targets (it packs with shape). And on 32-bit
422 * targets, adding slotSpan to JSObjectMap takes no gross extra space,
423 * because Shape rounds up to an even number of 32-bit words (required for
424 * GC-thing and js::Value allocation in any event) on 32-bit targets.
426 * So in terms of space, we can afford to maintain both slotSpan and slot,
427 * but it might be better if we eliminated slotSpan using slot combined
428 * with an auxiliary mechanism based on table.
430 void setParent(js::Shape *p) {
431 JS_STATIC_ASSERT(uint32(SHAPE_INVALID_SLOT) == ~uint32(0));
432 if (p)
433 slotSpan = JS_MAX(p->slotSpan, slot + 1);
434 JS_ASSERT(slotSpan < JSObject::NSLOTS_LIMIT);
435 parent = p;
438 void insertFree(js::Shape **freep) {
439 #ifdef DEBUG
440 memset(this, JS_FREE_PATTERN, sizeof *this);
441 #endif
442 id = JSID_VOID;
443 parent = *freep;
444 if (parent)
445 parent->listp = &parent;
446 listp = freep;
447 *freep = this;
450 void removeFree() {
451 JS_ASSERT(JSID_IS_VOID(id));
452 *listp = parent;
453 if (parent)
454 parent->listp = listp;
457 public:
458 const js::Shape *previous() const {
459 return parent;
462 class Range {
463 protected:
464 friend struct Shape;
466 const Shape *cursor;
467 const Shape *end;
469 public:
470 Range(const Shape *shape) : cursor(shape) { }
472 bool empty() const {
473 JS_ASSERT_IF(!cursor->parent, JSID_IS_EMPTY(cursor->id));
474 return !cursor->parent;
477 const Shape &front() const {
478 JS_ASSERT(!empty());
479 return *cursor;
482 void popFront() {
483 JS_ASSERT(!empty());
484 cursor = cursor->parent;
488 Range all() const {
489 return Range(this);
492 protected:
494 * Implementation-private bits stored in shape->flags. See public: enum {}
495 * flags further below, which were allocated FCFS over time, so interleave
496 * with these bits.
498 enum {
499 /* GC mark flag. */
500 MARK = 0x01,
502 SHARED_EMPTY = 0x02,
505 * Set during a shape-regenerating GC if the shape has already been
506 * regenerated.
508 SHAPE_REGEN = 0x04,
510 /* Property stored in per-object dictionary, not shared property tree. */
511 IN_DICTIONARY = 0x08,
513 /* Prevent unwanted mutation of shared Bindings::lastBinding nodes. */
514 FROZEN = 0x10
517 Shape(jsid id, js::PropertyOp getter, js::StrictPropertyOp setter, uint32 slot, uintN attrs,
518 uintN flags, intN shortid, uint32 shape = INVALID_SHAPE, uint32 slotSpan = 0);
520 /* Used by EmptyShape (see jsscopeinlines.h). */
521 Shape(JSCompartment *comp, Class *aclasp);
523 bool marked() const { return (flags & MARK) != 0; }
524 void mark() const { flags |= MARK; }
525 void clearMark() { flags &= ~MARK; }
527 bool hasRegenFlag() const { return (flags & SHAPE_REGEN) != 0; }
528 void setRegenFlag() { flags |= SHAPE_REGEN; }
529 void clearRegenFlag() { flags &= ~SHAPE_REGEN; }
531 bool inDictionary() const { return (flags & IN_DICTIONARY) != 0; }
532 bool frozen() const { return (flags & FROZEN) != 0; }
533 void setFrozen() { flags |= FROZEN; }
535 bool isEmptyShape() const { JS_ASSERT_IF(!parent, JSID_IS_EMPTY(id)); return !parent; }
537 public:
538 /* Public bits stored in shape->flags. */
539 enum {
540 ALIAS = 0x20,
541 HAS_SHORTID = 0x40,
542 METHOD = 0x80,
543 PUBLIC_FLAGS = ALIAS | HAS_SHORTID | METHOD
546 uintN getFlags() const { return flags & PUBLIC_FLAGS; }
547 bool isAlias() const { return (flags & ALIAS) != 0; }
548 bool hasShortID() const { return (flags & HAS_SHORTID) != 0; }
549 bool isMethod() const { return (flags & METHOD) != 0; }
551 JSObject &methodObject() const { JS_ASSERT(isMethod()); return *getterObj; }
553 js::PropertyOp getter() const { return rawGetter; }
554 bool hasDefaultGetter() const { return !rawGetter; }
555 js::PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return rawGetter; }
556 JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return getterObj; }
558 // Per ES5, decode null getterObj as the undefined value, which encodes as null.
559 js::Value getterValue() const {
560 JS_ASSERT(hasGetterValue());
561 return getterObj ? js::ObjectValue(*getterObj) : js::UndefinedValue();
564 js::Value getterOrUndefined() const {
565 return hasGetterValue() && getterObj ? js::ObjectValue(*getterObj) : js::UndefinedValue();
568 js::StrictPropertyOp setter() const { return rawSetter; }
569 bool hasDefaultSetter() const { return !rawSetter; }
570 js::StrictPropertyOp setterOp() const { JS_ASSERT(!hasSetterValue()); return rawSetter; }
571 JSObject *setterObject() const { JS_ASSERT(hasSetterValue()); return setterObj; }
573 // Per ES5, decode null setterObj as the undefined value, which encodes as null.
574 js::Value setterValue() const {
575 JS_ASSERT(hasSetterValue());
576 return setterObj ? js::ObjectValue(*setterObj) : js::UndefinedValue();
579 js::Value setterOrUndefined() const {
580 return hasSetterValue() && setterObj ? js::ObjectValue(*setterObj) : js::UndefinedValue();
583 inline JSDHashNumber hash() const;
584 inline bool matches(const js::Shape *p) const;
585 inline bool matchesParamsAfterId(js::PropertyOp agetter, js::StrictPropertyOp asetter,
586 uint32 aslot, uintN aattrs, uintN aflags,
587 intN ashortid) const;
589 bool get(JSContext* cx, JSObject *receiver, JSObject *obj, JSObject *pobj, js::Value* vp) const;
590 bool set(JSContext* cx, JSObject *obj, bool strict, js::Value* vp) const;
592 inline bool isSharedPermanent() const;
594 void trace(JSTracer *trc) const;
596 bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
598 uint8 attributes() const { return attrs; }
599 bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; }
600 bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; }
601 bool writable() const {
602 // JS_ASSERT(isDataDescriptor());
603 return (attrs & JSPROP_READONLY) == 0;
605 bool hasGetterValue() const { return attrs & JSPROP_GETTER; }
606 bool hasSetterValue() const { return attrs & JSPROP_SETTER; }
608 bool hasDefaultGetterOrIsMethod() const {
609 return hasDefaultGetter() || isMethod();
612 bool isDataDescriptor() const {
613 return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
615 bool isAccessorDescriptor() const {
616 return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
620 * For ES5 compatibility, we allow properties with JSPropertyOp-flavored
621 * setters to be shadowed when set. The "own" property thereby created in
622 * the directly referenced object will have the same getter and setter as
623 * the prototype property. See bug 552432.
625 bool shadowable() const {
626 JS_ASSERT_IF(isDataDescriptor(), writable());
627 return hasSlot() || (attrs & JSPROP_SHADOWABLE);
630 uint32 entryCount() const {
631 if (hasTable())
632 return getTable()->entryCount;
634 const js::Shape *shape = this;
635 uint32 count = 0;
636 for (js::Shape::Range r = shape->all(); !r.empty(); r.popFront())
637 ++count;
638 return count;
641 #ifdef DEBUG
642 void dump(JSContext *cx, FILE *fp) const;
643 void dumpSubtree(JSContext *cx, int level, FILE *fp) const;
644 #endif
647 struct EmptyShape : public js::Shape
649 EmptyShape(JSCompartment *comp, js::Class *aclasp);
651 js::Class *getClass() const { return clasp; };
653 static EmptyShape *create(JSCompartment *comp, js::Class *clasp) {
654 js::Shape *eprop = comp->propertyTree.newShapeUnchecked();
655 if (!eprop)
656 return NULL;
657 return new (eprop) EmptyShape(comp, clasp);
660 static EmptyShape *create(JSContext *cx, js::Class *clasp) {
661 js::Shape *eprop = JS_PROPERTY_TREE(cx).newShape(cx);
662 if (!eprop)
663 return NULL;
664 return new (eprop) EmptyShape(cx->compartment, clasp);
668 } /* namespace js */
670 /* js::Shape pointer tag bit indicating a collision. */
671 #define SHAPE_COLLISION (jsuword(1))
672 #define SHAPE_REMOVED ((js::Shape *) SHAPE_COLLISION)
674 /* Macros to get and set shape pointer values and collision flags. */
675 #define SHAPE_IS_FREE(shape) ((shape) == NULL)
676 #define SHAPE_IS_REMOVED(shape) ((shape) == SHAPE_REMOVED)
677 #define SHAPE_IS_LIVE(shape) ((shape) > SHAPE_REMOVED)
678 #define SHAPE_FLAG_COLLISION(spp,shape) (*(spp) = (js::Shape *) \
679 (jsuword(shape) | SHAPE_COLLISION))
680 #define SHAPE_HAD_COLLISION(shape) (jsuword(shape) & SHAPE_COLLISION)
681 #define SHAPE_FETCH(spp) SHAPE_CLEAR_COLLISION(*(spp))
683 #define SHAPE_CLEAR_COLLISION(shape) \
684 ((js::Shape *) (jsuword(shape) & ~SHAPE_COLLISION))
686 #define SHAPE_STORE_PRESERVING_COLLISION(spp, shape) \
687 (*(spp) = (js::Shape *) (jsuword(shape) | SHAPE_HAD_COLLISION(*(spp))))
689 inline js::Shape **
690 JSObject::nativeSearch(jsid id, bool adding)
692 return js::Shape::search(compartment()->rt, &lastProp, id, adding);
695 inline const js::Shape *
696 JSObject::nativeLookup(jsid id)
698 JS_ASSERT(isNative());
699 return SHAPE_FETCH(nativeSearch(id));
702 inline bool
703 JSObject::nativeContains(jsid id)
705 return nativeLookup(id) != NULL;
708 inline bool
709 JSObject::nativeContains(const js::Shape &shape)
711 return nativeLookup(shape.id) == &shape;
714 inline const js::Shape *
715 JSObject::lastProperty() const
717 JS_ASSERT(isNative());
718 JS_ASSERT(!JSID_IS_VOID(lastProp->id));
719 return lastProp;
722 inline bool
723 JSObject::nativeEmpty() const
725 return lastProperty()->isEmptyShape();
728 inline bool
729 JSObject::inDictionaryMode() const
731 return lastProperty()->inDictionary();
734 inline uint32
735 JSObject::propertyCount() const
737 return lastProperty()->entryCount();
740 inline bool
741 JSObject::hasPropertyTable() const
743 return lastProperty()->hasTable();
747 * FIXME: shape must not be null, should use a reference here and other places.
749 inline void
750 JSObject::setLastProperty(const js::Shape *shape)
752 JS_ASSERT(!inDictionaryMode());
753 JS_ASSERT(!JSID_IS_VOID(shape->id));
754 JS_ASSERT_IF(lastProp, !JSID_IS_VOID(lastProp->id));
755 JS_ASSERT(shape->compartment == compartment());
757 lastProp = const_cast<js::Shape *>(shape);
760 inline void
761 JSObject::removeLastProperty()
763 JS_ASSERT(!inDictionaryMode());
764 JS_ASSERT(!JSID_IS_VOID(lastProp->parent->id));
766 lastProp = lastProp->parent;
769 namespace js {
771 inline void
772 Shape::removeFromDictionary(JSObject *obj) const
774 JS_ASSERT(!frozen());
775 JS_ASSERT(inDictionary());
776 JS_ASSERT(obj->inDictionaryMode());
777 JS_ASSERT(listp);
778 JS_ASSERT(!JSID_IS_VOID(id));
780 JS_ASSERT(obj->lastProp->inDictionary());
781 JS_ASSERT(obj->lastProp->listp == &obj->lastProp);
782 JS_ASSERT_IF(obj->lastProp != this, !JSID_IS_VOID(obj->lastProp->id));
783 JS_ASSERT_IF(obj->lastProp->parent, !JSID_IS_VOID(obj->lastProp->parent->id));
785 if (parent)
786 parent->listp = listp;
787 *listp = parent;
788 listp = NULL;
791 inline void
792 Shape::insertIntoDictionary(js::Shape **dictp)
795 * Don't assert inDictionaryMode() here because we may be called from
796 * JSObject::toDictionaryMode via JSObject::newDictionaryShape.
798 JS_ASSERT(inDictionary());
799 JS_ASSERT(!listp);
800 JS_ASSERT(!JSID_IS_VOID(id));
802 JS_ASSERT_IF(*dictp, !(*dictp)->frozen());
803 JS_ASSERT_IF(*dictp, (*dictp)->inDictionary());
804 JS_ASSERT_IF(*dictp, (*dictp)->listp == dictp);
805 JS_ASSERT_IF(*dictp, !JSID_IS_VOID((*dictp)->id));
807 setParent(*dictp);
808 if (parent)
809 parent->listp = &parent;
810 listp = dictp;
811 *dictp = this;
814 } /* namespace js */
817 * If SHORTID is set in shape->flags, we use shape->shortid rather
818 * than id when calling shape's getter or setter.
820 #define SHAPE_USERID(shape) \
821 ((shape)->hasShortID() ? INT_TO_JSID((shape)->shortid) \
822 : (shape)->id)
824 extern uint32
825 js_GenerateShape(JSRuntime *rt);
827 extern uint32
828 js_GenerateShape(JSContext *cx);
830 #ifdef DEBUG
831 struct JSScopeStats {
832 jsrefcount searches;
833 jsrefcount hits;
834 jsrefcount misses;
835 jsrefcount hashes;
836 jsrefcount hashHits;
837 jsrefcount hashMisses;
838 jsrefcount steps;
839 jsrefcount stepHits;
840 jsrefcount stepMisses;
841 jsrefcount initSearches;
842 jsrefcount changeSearches;
843 jsrefcount tableAllocFails;
844 jsrefcount toDictFails;
845 jsrefcount wrapWatchFails;
846 jsrefcount adds;
847 jsrefcount addFails;
848 jsrefcount puts;
849 jsrefcount redundantPuts;
850 jsrefcount putFails;
851 jsrefcount changes;
852 jsrefcount changePuts;
853 jsrefcount changeFails;
854 jsrefcount compresses;
855 jsrefcount grows;
856 jsrefcount removes;
857 jsrefcount removeFrees;
858 jsrefcount uselessRemoves;
859 jsrefcount shrinks;
862 extern JS_FRIEND_DATA(JSScopeStats) js_scope_stats;
864 # define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x)
865 #else
866 # define METER(x) /* nothing */
867 #endif
869 namespace js {
871 JS_ALWAYS_INLINE js::Shape **
872 Shape::search(JSRuntime *rt, js::Shape **startp, jsid id, bool adding)
874 js::Shape *start = *startp;
875 METER(searches);
877 if (start->hasTable())
878 return start->getTable()->search(id, adding);
880 if (start->numLinearSearches == PropertyTable::MAX_LINEAR_SEARCHES) {
881 if (start->hashify(rt))
882 return start->getTable()->search(id, adding);
883 /* OOM! Don't increment numLinearSearches, to keep hasTable() false. */
884 JS_ASSERT(!start->hasTable());
885 } else {
886 JS_ASSERT(start->numLinearSearches < PropertyTable::MAX_LINEAR_SEARCHES);
887 start->numLinearSearches++;
891 * Not enough searches done so far to justify hashing: search linearly
892 * from *startp.
894 * We don't use a Range here, or stop at null parent (the empty shape
895 * at the end), to avoid an extra load per iteration just to save a
896 * load and id test at the end (when missing).
898 js::Shape **spp;
899 for (spp = startp; js::Shape *shape = *spp; spp = &shape->parent) {
900 if (shape->id == id) {
901 METER(hits);
902 return spp;
905 METER(misses);
906 return spp;
909 #undef METER
911 inline bool
912 Shape::isSharedPermanent() const
914 return (~attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0;
917 } // namespace js
919 #ifdef _MSC_VER
920 #pragma warning(pop)
921 #pragma warning(pop)
922 #endif
924 #endif /* jsscope_h___ */