Merge "fix: the incorrect version of tarball generated by gbs export" into tizen
[platform/upstream/js.git] / js / src / jsscope.h
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sw=4 et tw=99:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
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/
11  *
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.
16  *
17  * The Original Code is Mozilla Communicator client code, released
18  * March 31, 1998.
19  *
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.
24  *
25  * Contributor(s):
26  *
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.
38  *
39  * ***** END LICENSE BLOCK ***** */
40
41 #ifndef jsscope_h___
42 #define jsscope_h___
43 /*
44  * JS symbol tables.
45  */
46 #include <new>
47 #ifdef DEBUG
48 #include <stdio.h>
49 #endif
50
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"
60
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
67
68 /*
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.
75  *
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.
82  *
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).
89  *
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.)
96  *
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.
106  *
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.
116  *
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.
124  *
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.
130  *
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.
136  *
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.
140  *
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.
150  *
151  * Therefore,
152  *
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
157  *
158  * Note that P is alpha*T by definition, so
159  *
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)
165  *
166  * Let N = P*beta for a compression ratio beta, beta <= 1:
167  *
168  *      P*beta*(S+L) <> P * (1/alpha) * (S-1)
169  *      beta*(S+L)   <> (S-1)/alpha
170  *      beta         <> (S-1)/((S+L)*alpha)
171  *
172  * For S = 6 (32-bit architectures) and L = 2, the property tree wins iff
173  *
174  *      beta < 5/(8*alpha)
175  *
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.
178  *
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).
185  *
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).
192  *
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.
195  *
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.
201  *
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.
209  */
210
211 #define SHAPE_INVALID_SLOT              0xffffffff
212
213 namespace js {
214
215 /*
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.
220  */
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);
225
226     int             hashShift;          /* multiplicative hash shift */
227
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 */
234
235     PropertyTable(uint32 nentries)
236       : hashShift(JS_DHASH_BITS - MIN_SIZE_LOG2),
237         entryCount(nentries),
238         removedCount(0),
239         freelist(SHAPE_INVALID_SLOT)
240     {
241         /* NB: entries is set by init, which must be called. */
242     }
243
244     ~PropertyTable() {
245         js_free(entries);
246     }
247
248     /* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */
249     uint32 capacity() const { return JS_BIT(JS_DHASH_BITS - hashShift); }
250
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);
255     }
256
257     /*
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.
261      */
262     bool grow(JSContext *cx);
263
264     /*
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.
268      */
269     bool            init(JSRuntime *rt, js::Shape *lastProp);
270     bool            change(int log2Delta, JSContext *cx);
271     js::Shape       **search(jsid id, bool adding);
272 };
273
274 } /* namespace js */
275
276 struct JSObject;
277
278 namespace js {
279
280 class PropertyTree;
281
282 static inline PropertyOp
283 CastAsPropertyOp(js::Class *clasp)
284 {
285     return JS_DATA_TO_FUNC_PTR(PropertyOp, clasp);
286 }
287
288 /*
289  * Reuse the API-only JSPROP_INDEX attribute to mean shadowability.
290  */
291 #define JSPROP_SHADOWABLE       JSPROP_INDEX
292
293 struct Shape : public JSObjectMap
294 {
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);
300
301     /* 
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.
308      */
309     union {
310         mutable size_t numLinearSearches;
311         mutable js::PropertyTable *table;
312     };
313
314   public:
315     inline void freeTable(JSContext *cx);
316
317     static bool initEmptyShapes(JSCompartment *comp);
318     static void finishEmptyShapes(JSCompartment *comp);
319
320     jsid                id;
321
322 #ifdef DEBUG
323     JSCompartment       *compartment;
324 #endif
325
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 */
334     };
335
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() */
341     };
342
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 */
350
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 */
360     };
361
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);
366
367     inline void removeFromDictionary(JSObject *obj) const;
368     inline void insertIntoDictionary(js::Shape **dictp);
369
370     js::Shape *getChild(JSContext *cx, const js::Shape &child, js::Shape **listp);
371
372     bool hashify(JSRuntime *rt);
373
374     bool hasTable() const {
375         /* A valid pointer should be much bigger than MAX_LINEAR_SEARCHES. */
376         return numLinearSearches > PropertyTable::MAX_LINEAR_SEARCHES;
377     }
378
379     js::PropertyTable *getTable() const {
380         JS_ASSERT(hasTable());
381         return table;
382     }
383
384     void setTable(js::PropertyTable *t) const {
385         JS_ASSERT_IF(t && t->freelist != SHAPE_INVALID_SLOT, t->freelist < slotSpan);
386         table = t;
387     }
388
389     /*
390      * Setter for parent. The challenge is to maintain JSObjectMap::slotSpan in
391      * the face of arbitrary slot order.
392      *
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.
396      *
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.
402      *
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).
408      *
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.
415      *
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.
419      *
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.
425      *
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.
429      */
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;
436     }
437
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;
448     }
449
450     void removeFree() {
451         JS_ASSERT(JSID_IS_VOID(id));
452         *listp = parent;
453         if (parent)
454             parent->listp = listp;
455     }
456
457   public:
458     const js::Shape *previous() const {
459         return parent;
460     }
461
462     class Range {
463       protected:
464         friend struct Shape;
465
466         const Shape *cursor;
467         const Shape *end;
468
469       public:
470         Range(const Shape *shape) : cursor(shape) { }
471
472         bool empty() const {
473             JS_ASSERT_IF(!cursor->parent, JSID_IS_EMPTY(cursor->id));
474             return !cursor->parent;
475         }
476
477         const Shape &front() const {
478             JS_ASSERT(!empty());
479             return *cursor;
480         }
481
482         void popFront() {
483             JS_ASSERT(!empty());
484             cursor = cursor->parent;
485         }
486     };
487
488     Range all() const {
489         return Range(this);
490     }
491
492   protected:
493     /*
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.
497      */
498     enum {
499         /* GC mark flag. */
500         MARK            = 0x01,
501
502         SHARED_EMPTY    = 0x02,
503
504         /*
505          * Set during a shape-regenerating GC if the shape has already been
506          * regenerated.
507          */
508         SHAPE_REGEN     = 0x04,
509
510         /* Property stored in per-object dictionary, not shared property tree. */
511         IN_DICTIONARY   = 0x08,
512
513         /* Prevent unwanted mutation of shared Bindings::lastBinding nodes. */
514         FROZEN          = 0x10
515     };
516
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);
519
520     /* Used by EmptyShape (see jsscopeinlines.h). */
521     Shape(JSCompartment *comp, Class *aclasp);
522
523     bool marked() const         { return (flags & MARK) != 0; }
524     void mark() const           { flags |= MARK; }
525     void clearMark()            { flags &= ~MARK; }
526
527     bool hasRegenFlag() const   { return (flags & SHAPE_REGEN) != 0; }
528     void setRegenFlag()         { flags |= SHAPE_REGEN; }
529     void clearRegenFlag()       { flags &= ~SHAPE_REGEN; }
530
531     bool inDictionary() const   { return (flags & IN_DICTIONARY) != 0; }
532     bool frozen() const         { return (flags & FROZEN) != 0; }
533     void setFrozen()            { flags |= FROZEN; }
534
535     bool isEmptyShape() const   { JS_ASSERT_IF(!parent, JSID_IS_EMPTY(id)); return !parent; }
536
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
544     };
545
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; }
550
551     JSObject &methodObject() const { JS_ASSERT(isMethod()); return *getterObj; }
552
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; }
557
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();
562     }
563
564     js::Value getterOrUndefined() const {
565         return hasGetterValue() && getterObj ? js::ObjectValue(*getterObj) : js::UndefinedValue();
566     }
567
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; }
572
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();
577     }
578
579     js::Value setterOrUndefined() const {
580         return hasSetterValue() && setterObj ? js::ObjectValue(*setterObj) : js::UndefinedValue();
581     }
582
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;
588
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;
591
592     inline bool isSharedPermanent() const;
593
594     void trace(JSTracer *trc) const;
595
596     bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
597
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;
604     }
605     bool hasGetterValue() const { return attrs & JSPROP_GETTER; }
606     bool hasSetterValue() const { return attrs & JSPROP_SETTER; }
607
608     bool hasDefaultGetterOrIsMethod() const {
609         return hasDefaultGetter() || isMethod();
610     }
611
612     bool isDataDescriptor() const {
613         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
614     }
615     bool isAccessorDescriptor() const {
616         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
617     }
618
619     /*
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.
624      */
625     bool shadowable() const {
626         JS_ASSERT_IF(isDataDescriptor(), writable());
627         return hasSlot() || (attrs & JSPROP_SHADOWABLE);
628     }
629
630     uint32 entryCount() const {
631         if (hasTable())
632             return getTable()->entryCount;
633
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;
639     }
640
641 #ifdef DEBUG
642     void dump(JSContext *cx, FILE *fp) const;
643     void dumpSubtree(JSContext *cx, int level, FILE *fp) const;
644 #endif
645 };
646
647 struct EmptyShape : public js::Shape
648 {
649     EmptyShape(JSCompartment *comp, js::Class *aclasp);
650
651     js::Class *getClass() const { return clasp; };
652
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);
658     }
659
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);
665     }
666 };
667
668 } /* namespace js */
669
670 /* js::Shape pointer tag bit indicating a collision. */
671 #define SHAPE_COLLISION                 (jsuword(1))
672 #define SHAPE_REMOVED                   ((js::Shape *) SHAPE_COLLISION)
673
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))
682
683 #define SHAPE_CLEAR_COLLISION(shape)                                          \
684     ((js::Shape *) (jsuword(shape) & ~SHAPE_COLLISION))
685
686 #define SHAPE_STORE_PRESERVING_COLLISION(spp, shape)                          \
687     (*(spp) = (js::Shape *) (jsuword(shape) | SHAPE_HAD_COLLISION(*(spp))))
688
689 inline js::Shape **
690 JSObject::nativeSearch(jsid id, bool adding)
691 {
692     return js::Shape::search(compartment()->rt, &lastProp, id, adding);
693 }
694
695 inline const js::Shape *
696 JSObject::nativeLookup(jsid id)
697 {
698     JS_ASSERT(isNative());
699     return SHAPE_FETCH(nativeSearch(id));
700 }
701
702 inline bool
703 JSObject::nativeContains(jsid id)
704 {
705     return nativeLookup(id) != NULL;
706 }
707
708 inline bool
709 JSObject::nativeContains(const js::Shape &shape)
710 {
711     return nativeLookup(shape.id) == &shape;
712 }
713
714 inline const js::Shape *
715 JSObject::lastProperty() const
716 {
717     JS_ASSERT(isNative());
718     JS_ASSERT(!JSID_IS_VOID(lastProp->id));
719     return lastProp;
720 }
721
722 inline bool
723 JSObject::nativeEmpty() const
724 {
725     return lastProperty()->isEmptyShape();
726 }
727
728 inline bool
729 JSObject::inDictionaryMode() const
730 {
731     return lastProperty()->inDictionary();
732 }
733
734 inline uint32
735 JSObject::propertyCount() const
736 {
737     return lastProperty()->entryCount();
738 }
739
740 inline bool
741 JSObject::hasPropertyTable() const
742 {
743     return lastProperty()->hasTable();
744 }
745
746 /*
747  * FIXME: shape must not be null, should use a reference here and other places.
748  */
749 inline void
750 JSObject::setLastProperty(const js::Shape *shape)
751 {
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());
756
757     lastProp = const_cast<js::Shape *>(shape);
758 }
759
760 inline void
761 JSObject::removeLastProperty()
762 {
763     JS_ASSERT(!inDictionaryMode());
764     JS_ASSERT(!JSID_IS_VOID(lastProp->parent->id));
765
766     lastProp = lastProp->parent;
767 }
768
769 namespace js {
770
771 inline void
772 Shape::removeFromDictionary(JSObject *obj) const
773 {
774     JS_ASSERT(!frozen());
775     JS_ASSERT(inDictionary());
776     JS_ASSERT(obj->inDictionaryMode());
777     JS_ASSERT(listp);
778     JS_ASSERT(!JSID_IS_VOID(id));
779
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));
784
785     if (parent)
786         parent->listp = listp;
787     *listp = parent;
788     listp = NULL;
789 }
790
791 inline void
792 Shape::insertIntoDictionary(js::Shape **dictp)
793 {
794     /*
795      * Don't assert inDictionaryMode() here because we may be called from
796      * JSObject::toDictionaryMode via JSObject::newDictionaryShape.
797      */
798     JS_ASSERT(inDictionary());
799     JS_ASSERT(!listp);
800     JS_ASSERT(!JSID_IS_VOID(id));
801
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));
806
807     setParent(*dictp);
808     if (parent)
809         parent->listp = &parent;
810     listp = dictp;
811     *dictp = this;
812 }
813
814 } /* namespace js */
815
816 /*
817  * If SHORTID is set in shape->flags, we use shape->shortid rather
818  * than id when calling shape's getter or setter.
819  */
820 #define SHAPE_USERID(shape)                                                   \
821     ((shape)->hasShortID() ? INT_TO_JSID((shape)->shortid)                    \
822                            : (shape)->id)
823
824 extern uint32
825 js_GenerateShape(JSRuntime *rt);
826
827 extern uint32
828 js_GenerateShape(JSContext *cx);
829
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;
860 };
861
862 extern JS_FRIEND_DATA(JSScopeStats) js_scope_stats;
863
864 # define METER(x)       JS_ATOMIC_INCREMENT(&js_scope_stats.x)
865 #else
866 # define METER(x)       /* nothing */
867 #endif
868
869 namespace js {
870
871 JS_ALWAYS_INLINE js::Shape **
872 Shape::search(JSRuntime *rt, js::Shape **startp, jsid id, bool adding)
873 {
874     js::Shape *start = *startp;
875     METER(searches);
876
877     if (start->hasTable())
878         return start->getTable()->search(id, adding);
879
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++;
888     }
889
890     /*
891      * Not enough searches done so far to justify hashing: search linearly
892      * from *startp.
893      *
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).
897      */
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;
903         }
904     }
905     METER(misses);
906     return spp;
907 }
908
909 #undef METER
910
911 inline bool
912 Shape::isSharedPermanent() const
913 {
914     return (~attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0;
915 }
916
917 } // namespace js
918
919 #ifdef _MSC_VER
920 #pragma warning(pop)
921 #pragma warning(pop)
922 #endif
923
924 #endif /* jsscope_h___ */