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 "jsobj.h"
52 : #include "jspropertytree.h"
53 : #include "jstypes.h"
54 :
55 : #include "js/HashTable.h"
56 : #include "gc/Root.h"
57 : #include "mozilla/Attributes.h"
58 :
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
65 :
66 : /*
67 : * In isolation, a Shape represents a property that exists in one or more
68 : * objects; it has an id, flags, etc. (But it doesn't represent the property's
69 : * value.) However, Shapes are always stored in linked linear sequence of
70 : * Shapes, called "shape lineages". Each shape lineage represents the layout of
71 : * an entire object.
72 : *
73 : * Every JSObject has a pointer, |shape_|, accessible via lastProperty(), to
74 : * the last Shape in a shape lineage, which identifies the property most
75 : * recently added to the object. This pointer permits fast object layout
76 : * tests. The shape lineage order also dictates the enumeration order for the
77 : * object; ECMA requires no particular order but this implementation has
78 : * promised and delivered property definition order.
79 : *
80 : * Shape lineages occur in two kinds of data structure.
81 : *
82 : * 1. N-ary property trees. Each path from a non-root node to the root node in
83 : * a property tree is a shape lineage. Property trees permit full (or
84 : * partial) sharing of Shapes between objects that have fully (or partly)
85 : * identical layouts. The root is an EmptyShape whose identity is determined
86 : * by the object's class, compartment and prototype. These Shapes are shared
87 : * and immutable.
88 : *
89 : * 2. Dictionary mode lists. Shapes in such lists are said to be "in
90 : * dictionary mode", as are objects that point to such Shapes. These Shapes
91 : * are unshared, private to a single object, and immutable except for their
92 : * links in the dictionary list.
93 : *
94 : * All shape lineages are bi-directionally linked, via the |parent| and
95 : * |kids|/|listp| members.
96 : *
97 : * Shape lineages start out life in the property tree. They can be converted
98 : * (by copying) to dictionary mode lists in the following circumstances.
99 : *
100 : * 1. The shape lineage's size reaches MAX_HEIGHT. This reasonable limit avoids
101 : * potential worst cases involving shape lineage mutations.
102 : *
103 : * 2. A property represented by a non-last Shape in a shape lineage is removed
104 : * from an object. (In the last Shape case, obj->shape_ can be easily
105 : * adjusted to point to obj->shape_->parent.) We originally tried lazy
106 : * forking of the property tree, but this blows up for delete/add
107 : * repetitions.
108 : *
109 : * 3. A property represented by a non-last Shape in a shape lineage has its
110 : * attributes modified.
111 : *
112 : * To find the Shape for a particular property of an object initially requires
113 : * a linear search. But if the number of searches starting at any particular
114 : * Shape in the property tree exceeds MAX_LINEAR_SEARCHES and the Shape's
115 : * lineage has (excluding the EmptyShape) at least MIN_ENTRIES, we create an
116 : * auxiliary hash table -- the PropertyTable -- that allows faster lookup.
117 : * Furthermore, a PropertyTable is always created for dictionary mode lists,
118 : * and it is attached to the last Shape in the lineage. Property tables for
119 : * property tree Shapes never change, but property tables for dictionary mode
120 : * Shapes can grow and shrink.
121 : *
122 : * There used to be a long, math-heavy comment here explaining why property
123 : * trees are more space-efficient than alternatives. This was removed in bug
124 : * 631138; see that bug for the full details.
125 : *
126 : * Because many Shapes have similar data, there is actually a secondary type
127 : * called a BaseShape that holds some of a Shape's data. Many shapes can share
128 : * a single BaseShape.
129 : */
130 :
131 : namespace js {
132 :
133 : /* Limit on the number of slotful properties in an object. */
134 : static const uint32_t SHAPE_INVALID_SLOT = JS_BIT(24) - 1;
135 : static const uint32_t SHAPE_MAXIMUM_SLOT = JS_BIT(24) - 2;
136 :
137 : /*
138 : * Shapes use multiplicative hashing, but specialized to
139 : * minimize footprint.
140 : */
141 : struct PropertyTable {
142 : static const uint32_t HASH_BITS = tl::BitSize<HashNumber>::result;
143 : static const uint32_t MIN_ENTRIES = 7;
144 : static const uint32_t MIN_SIZE_LOG2 = 4;
145 : static const uint32_t MIN_SIZE = JS_BIT(MIN_SIZE_LOG2);
146 :
147 : int hashShift; /* multiplicative hash shift */
148 :
149 : uint32_t entryCount; /* number of entries in table */
150 : uint32_t removedCount; /* removed entry sentinels in table */
151 : uint32_t freelist; /* SHAPE_INVALID_SLOT or head of slot
152 : freelist in owning dictionary-mode
153 : object */
154 : js::Shape **entries; /* table of ptrs to shared tree nodes */
155 :
156 127390 : PropertyTable(uint32_t nentries)
157 : : hashShift(HASH_BITS - MIN_SIZE_LOG2),
158 : entryCount(nentries),
159 : removedCount(0),
160 127390 : freelist(SHAPE_INVALID_SLOT)
161 : {
162 : /* NB: entries is set by init, which must be called. */
163 127390 : }
164 :
165 127390 : ~PropertyTable() {
166 127390 : js::UnwantedForeground::free_(entries);
167 127390 : }
168 :
169 : /* By definition, hashShift = HASH_BITS - log2(capacity). */
170 1654145 : uint32_t capacity() const { return JS_BIT(HASH_BITS - hashShift); }
171 :
172 : /* Computes the size of the entries array for a given capacity. */
173 132187 : static size_t sizeOfEntries(size_t cap) { return cap * sizeof(Shape *); }
174 :
175 : /*
176 : * This counts the PropertyTable object itself (which must be
177 : * heap-allocated) and its |entries| array.
178 : */
179 : size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
180 : return mallocSizeOf(this) + mallocSizeOf(entries);
181 : }
182 :
183 : /* Whether we need to grow. We want to do this if the load factor is >= 0.75 */
184 1646936 : bool needsToGrow() const {
185 1646936 : uint32_t size = capacity();
186 1646936 : return entryCount + removedCount >= size - (size >> 2);
187 : }
188 :
189 : /*
190 : * Try to grow the table. On failure, reports out of memory on cx
191 : * and returns false. This will make any extant pointers into the
192 : * table invalid. Don't call this unless needsToGrow() is true.
193 : */
194 : bool grow(JSContext *cx);
195 :
196 : /*
197 : * NB: init and change are fallible but do not report OOM, so callers can
198 : * cope or ignore. They do however use JSRuntime's calloc_ method in order
199 : * to update the malloc counter on success.
200 : */
201 : bool init(JSRuntime *rt, js::Shape *lastProp);
202 : bool change(int log2Delta, JSContext *cx);
203 : js::Shape **search(jsid id, bool adding);
204 : };
205 :
206 : } /* namespace js */
207 :
208 : struct JSObject;
209 :
210 : namespace js {
211 :
212 : class PropertyTree;
213 :
214 : /*
215 : * Reuse the API-only JSPROP_INDEX attribute to mean shadowability.
216 : */
217 : #define JSPROP_SHADOWABLE JSPROP_INDEX
218 :
219 : /*
220 : * Shapes encode information about both a property lineage *and* a particular
221 : * property. This information is split across the Shape and the BaseShape
222 : * at shape->base(). Both Shape and BaseShape can be either owned or unowned
223 : * by, respectively, the Object or Shape referring to them.
224 : *
225 : * Owned Shapes are used in dictionary objects, and form a doubly linked list
226 : * whose entries are all owned by that dictionary. Unowned Shapes are all in
227 : * the property tree.
228 : *
229 : * Owned BaseShapes are used for shapes which have property tables, including
230 : * the last properties in all dictionaries. Unowned BaseShapes compactly store
231 : * information common to many shapes. In a given compartment there is a single
232 : * BaseShape for each combination of BaseShape information. This information
233 : * is cloned in owned BaseShapes so that information can be quickly looked up
234 : * for a given object or shape without regard to whether the base shape is
235 : * owned or not.
236 : *
237 : * All combinations of owned/unowned Shapes/BaseShapes are possible:
238 : *
239 : * Owned Shape, Owned BaseShape:
240 : *
241 : * Last property in a dictionary object. The BaseShape is transferred from
242 : * property to property as the object's last property changes.
243 : *
244 : * Owned Shape, Unowned BaseShape:
245 : *
246 : * Property in a dictionary object other than the last one.
247 : *
248 : * Unowned Shape, Owned BaseShape:
249 : *
250 : * Property in the property tree which has a property table.
251 : *
252 : * Unowned Shape, Unowned BaseShape:
253 : *
254 : * Property in the property tree which does not have a property table.
255 : *
256 : * BaseShapes additionally encode some information about the referring object
257 : * itself. This includes the object's class, parent and various flags that may
258 : * be set for the object. Except for the class, this information is mutable and
259 : * may change when the object has an established property lineage. On such
260 : * changes the entire property lineage is not updated, but rather only the
261 : * last property (and its base shape). This works because only the object's
262 : * last property is used to query information about the object. Care must be
263 : * taken to call JSObject::canRemoveLastProperty when unwinding an object to
264 : * an earlier property, however.
265 : */
266 :
267 : class UnownedBaseShape;
268 :
269 : class BaseShape : public js::gc::Cell
270 127390 : {
271 : public:
272 : friend struct Shape;
273 : friend struct StackBaseShape;
274 : friend struct StackShape;
275 :
276 : enum Flag {
277 : /* Owned by the referring shape. */
278 : OWNED_SHAPE = 0x1,
279 :
280 : /* getterObj/setterObj are active in unions below. */
281 : HAS_GETTER_OBJECT = 0x2,
282 : HAS_SETTER_OBJECT = 0x4,
283 :
284 : /*
285 : * Flags set which describe the referring object. Once set these cannot
286 : * be unset, and are transferred from shape to shape as the object's
287 : * last property changes.
288 : */
289 :
290 : EXTENSIBLE_PARENTS = 0x8,
291 : DELEGATE = 0x10,
292 : SYSTEM = 0x20,
293 : NOT_EXTENSIBLE = 0x40,
294 : INDEXED = 0x80,
295 : BOUND_FUNCTION = 0x100,
296 : VAROBJ = 0x200,
297 : WATCHED = 0x400,
298 : ITERATED_SINGLETON = 0x800,
299 : NEW_TYPE_UNKNOWN = 0x1000,
300 : UNCACHEABLE_PROTO = 0x2000,
301 :
302 : OBJECT_FLAG_MASK = 0x3ff8
303 : };
304 :
305 : private:
306 : Class *clasp; /* Class of referring object. */
307 : HeapPtrObject parent; /* Parent of referring object. */
308 : uint32_t flags; /* Vector of above flags. */
309 : uint32_t slotSpan_; /* Object slot span for BaseShapes at
310 : * dictionary last properties. */
311 :
312 : union {
313 : js::PropertyOp rawGetter; /* getter hook for shape */
314 : JSObject *getterObj; /* user-defined callable "get" object or
315 : null if shape->hasGetterValue() */
316 : };
317 :
318 : union {
319 : js::StrictPropertyOp rawSetter; /* setter hook for shape */
320 : JSObject *setterObj; /* user-defined callable "set" object or
321 : null if shape->hasSetterValue() */
322 : };
323 :
324 : /* For owned BaseShapes, the canonical unowned BaseShape. */
325 : HeapPtr<UnownedBaseShape> unowned_;
326 :
327 : /* For owned BaseShapes, the shape's property table. */
328 : PropertyTable *table_;
329 :
330 : public:
331 : void finalize(FreeOp *fop);
332 :
333 : inline BaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags);
334 : inline BaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags,
335 : uint8_t attrs, PropertyOp rawGetter, StrictPropertyOp rawSetter);
336 : inline BaseShape(const StackBaseShape &base);
337 :
338 : /* Not defined: BaseShapes must not be stack allocated. */
339 : ~BaseShape();
340 :
341 : inline BaseShape &operator=(const BaseShape &other);
342 :
343 470833332 : bool isOwned() const { return !!(flags & OWNED_SHAPE); }
344 :
345 : inline bool matchesGetterSetter(PropertyOp rawGetter,
346 : StrictPropertyOp rawSetter) const;
347 :
348 : inline void adoptUnowned(UnownedBaseShape *other);
349 : inline void setOwned(UnownedBaseShape *unowned);
350 :
351 10812663 : JSObject *getObjectParent() const { return parent; }
352 27372082 : uint32_t getObjectFlags() const { return flags & OBJECT_FLAG_MASK; }
353 :
354 16717511 : bool hasGetterObject() const { return !!(flags & HAS_GETTER_OBJECT); }
355 2385992 : JSObject *getterObject() const { JS_ASSERT(hasGetterObject()); return getterObj; }
356 :
357 14639127 : bool hasSetterObject() const { return !!(flags & HAS_SETTER_OBJECT); }
358 307608 : JSObject *setterObject() const { JS_ASSERT(hasSetterObject()); return setterObj; }
359 :
360 158781828 : bool hasTable() const { JS_ASSERT_IF(table_, isOwned()); return table_ != NULL; }
361 57771371 : PropertyTable &table() const { JS_ASSERT(table_ && isOwned()); return *table_; }
362 2143271 : void setTable(PropertyTable *table) { JS_ASSERT(isOwned()); table_ = table; }
363 :
364 224789549 : uint32_t slotSpan() const { JS_ASSERT(isOwned()); return slotSpan_; }
365 3431432 : void setSlotSpan(uint32_t slotSpan) { JS_ASSERT(isOwned()); slotSpan_ = slotSpan; }
366 :
367 : /* Lookup base shapes from the compartment's baseShapes table. */
368 : static UnownedBaseShape *getUnowned(JSContext *cx, const StackBaseShape &base);
369 :
370 : /* Get the canonical base shape. */
371 : inline UnownedBaseShape *unowned();
372 :
373 : /* Get the canonical base shape for an owned one. */
374 : inline UnownedBaseShape *baseUnowned();
375 :
376 : /* Get the canonical base shape for an unowned one (i.e. identity). */
377 : inline UnownedBaseShape *toUnowned();
378 :
379 : /* Check that an owned base shape is consistent with its unowned base. */
380 : inline void assertConsistency();
381 :
382 : /* For JIT usage */
383 113046 : static inline size_t offsetOfClass() { return offsetof(BaseShape, clasp); }
384 3075 : static inline size_t offsetOfParent() { return offsetof(BaseShape, parent); }
385 1615 : static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); }
386 :
387 : static inline void writeBarrierPre(BaseShape *shape);
388 : static inline void writeBarrierPost(BaseShape *shape, void *addr);
389 : static inline void readBarrier(BaseShape *shape);
390 :
391 : static inline ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; }
392 :
393 : inline void markChildren(JSTracer *trc);
394 :
395 : private:
396 : static void staticAsserts() {
397 : JS_STATIC_ASSERT(offsetof(BaseShape, clasp) == offsetof(js::shadow::BaseShape, clasp));
398 : }
399 : };
400 :
401 : class UnownedBaseShape : public BaseShape {};
402 :
403 : UnownedBaseShape *
404 72237867 : BaseShape::unowned()
405 : {
406 72237867 : return isOwned() ? baseUnowned() : toUnowned();
407 : }
408 :
409 : UnownedBaseShape *
410 71823533 : BaseShape::toUnowned()
411 : {
412 71823533 : JS_ASSERT(!isOwned() && !unowned_); return static_cast<UnownedBaseShape *>(this);
413 : }
414 :
415 : UnownedBaseShape *
416 7237667 : BaseShape::baseUnowned()
417 : {
418 7237667 : JS_ASSERT(isOwned() && unowned_); return unowned_;
419 : }
420 :
421 : /* Entries for the per-compartment baseShapes set of unowned base shapes. */
422 : struct StackBaseShape
423 : {
424 : typedef const StackBaseShape *Lookup;
425 :
426 : uint32_t flags;
427 : Class *clasp;
428 : JSObject *parent;
429 : PropertyOp rawGetter;
430 : StrictPropertyOp rawSetter;
431 :
432 15910736 : StackBaseShape(BaseShape *base)
433 : : flags(base->flags & BaseShape::OBJECT_FLAG_MASK),
434 : clasp(base->clasp),
435 : parent(base->parent),
436 : rawGetter(NULL),
437 15910736 : rawSetter(NULL)
438 15910736 : {}
439 :
440 2215383 : StackBaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags)
441 : : flags(objectFlags),
442 : clasp(clasp),
443 : parent(parent),
444 : rawGetter(NULL),
445 2215383 : rawSetter(NULL)
446 2215383 : {}
447 :
448 : inline StackBaseShape(Shape *shape);
449 :
450 : inline void updateGetterSetter(uint8_t attrs,
451 : PropertyOp rawGetter,
452 : StrictPropertyOp rawSetter);
453 :
454 : static inline HashNumber hash(const StackBaseShape *lookup);
455 : static inline bool match(UnownedBaseShape *key, const StackBaseShape *lookup);
456 : };
457 :
458 : typedef HashSet<ReadBarriered<UnownedBaseShape>,
459 : StackBaseShape,
460 : SystemAllocPolicy> BaseShapeSet;
461 :
462 : struct Shape : public js::gc::Cell
463 0 : {
464 : friend struct ::JSObject;
465 : friend struct ::JSFunction;
466 : friend class js::Bindings;
467 : friend class js::ObjectImpl;
468 : friend class js::PropertyTree;
469 : friend class js::StaticBlockObject;
470 : friend struct js::StackShape;
471 : friend struct js::StackBaseShape;
472 :
473 : protected:
474 : HeapPtrBaseShape base_;
475 : HeapId propid_;
476 :
477 : JS_ENUM_HEADER(SlotInfo, uint32_t)
478 : {
479 : /* Number of fixed slots in objects with this shape. */
480 : FIXED_SLOTS_MAX = 0x1f,
481 : FIXED_SLOTS_SHIFT = 27,
482 : FIXED_SLOTS_MASK = uint32_t(FIXED_SLOTS_MAX << FIXED_SLOTS_SHIFT),
483 :
484 : /*
485 : * numLinearSearches starts at zero and is incremented initially on
486 : * search() calls. Once numLinearSearches reaches LINEAR_SEARCHES_MAX,
487 : * the table is created on the next search() call. The table can also
488 : * be created when hashifying for dictionary mode.
489 : */
490 : LINEAR_SEARCHES_MAX = 0x7,
491 : LINEAR_SEARCHES_SHIFT = 24,
492 : LINEAR_SEARCHES_MASK = LINEAR_SEARCHES_MAX << LINEAR_SEARCHES_SHIFT,
493 :
494 : /*
495 : * Mask to get the index in object slots for shapes which hasSlot().
496 : * For !hasSlot() shapes in the property tree with a parent, stores the
497 : * parent's slot index (which may be invalid), and invalid for all
498 : * other shapes.
499 : */
500 : SLOT_MASK = JS_BIT(24) - 1
501 : } JS_ENUM_FOOTER(SlotInfo);
502 :
503 : uint32_t slotInfo; /* mask of above info */
504 : uint8_t attrs; /* attributes, see jsapi.h JSPROP_* */
505 : uint8_t flags; /* flags, see below for defines */
506 : int16_t shortid_; /* tinyid, or local arg/var index */
507 :
508 : HeapPtrShape parent; /* parent node, reverse for..in order */
509 : /* kids is valid when !inDictionary(), listp is valid when inDictionary(). */
510 : union {
511 : KidsPointer kids; /* null, single child, or a tagged ptr
512 : to many-kids data structure */
513 : HeapPtrShape *listp; /* dictionary list starting at shape_
514 : has a double-indirect back pointer,
515 : either to the next shape's parent if not
516 : last, else to obj->shape_ */
517 : };
518 :
519 : static inline Shape *search(JSContext *cx, Shape *start, jsid id,
520 : Shape ***pspp, bool adding = false);
521 :
522 : inline void removeFromDictionary(JSObject *obj);
523 : inline void insertIntoDictionary(HeapPtrShape *dictp);
524 :
525 : inline void initDictionaryShape(const StackShape &child, uint32_t nfixed,
526 : HeapPtrShape *dictp);
527 :
528 : Shape *getChildBinding(JSContext *cx, const StackShape &child);
529 :
530 : /* Replace the base shape of the last shape in a non-dictionary lineage with base. */
531 : static Shape *replaceLastProperty(JSContext *cx, const StackBaseShape &base,
532 : JSObject *proto, Shape *shape);
533 :
534 : bool hashify(JSContext *cx);
535 : void handoffTableTo(Shape *newShape);
536 :
537 : inline void setParent(js::Shape *p);
538 :
539 127390 : bool ensureOwnBaseShape(JSContext *cx) {
540 127390 : if (base()->isOwned())
541 0 : return true;
542 127390 : return makeOwnBaseShape(cx);
543 : }
544 :
545 : bool makeOwnBaseShape(JSContext *cx);
546 :
547 : public:
548 158781828 : bool hasTable() const { return base()->hasTable(); }
549 55755490 : js::PropertyTable &table() const { return base()->table(); }
550 :
551 : void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf,
552 : size_t *propTableSize, size_t *kidsSize) const {
553 : *propTableSize = hasTable() ? table().sizeOfIncludingThis(mallocSizeOf) : 0;
554 : *kidsSize = !inDictionary() && kids.isHash()
555 : ? kids.toHash()->sizeOfIncludingThis(mallocSizeOf)
556 : : 0;
557 : }
558 :
559 312729946 : bool isNative() const {
560 312729946 : JS_ASSERT(!(flags & NON_NATIVE) == getObjectClass()->isNative());
561 312729946 : return !(flags & NON_NATIVE);
562 : }
563 :
564 26270450 : const HeapPtrShape &previous() const {
565 26270450 : return parent;
566 : }
567 :
568 : class Range {
569 : protected:
570 : friend struct Shape;
571 : const Shape *cursor;
572 :
573 : public:
574 67587088 : Range(const Shape *shape) : cursor(shape) { }
575 :
576 -1618075960 : bool empty() const {
577 -1618075960 : return cursor->isEmptyShape();
578 : }
579 :
580 41981912 : const Shape &front() const {
581 41981912 : JS_ASSERT(!empty());
582 41981912 : return *cursor;
583 : }
584 :
585 1283651952 : void popFront() {
586 1283651952 : JS_ASSERT(!empty());
587 1283651952 : cursor = cursor->parent;
588 1283651952 : }
589 :
590 2069854 : class Root {
591 : js::Root<const Shape*> cursorRoot;
592 : public:
593 2069854 : Root(JSContext *cx, Range *range)
594 2069854 : : cursorRoot(cx, &range->cursor)
595 2069854 : {}
596 : };
597 : };
598 :
599 66839310 : Range all() const {
600 66839310 : return Range(this);
601 : }
602 :
603 -1698948099 : Class *getObjectClass() const { return base()->clasp; }
604 1054888195 : JSObject *getObjectParent() const { return base()->parent; }
605 :
606 : static Shape *setObjectParent(JSContext *cx, JSObject *obj, JSObject *proto, Shape *last);
607 : static Shape *setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, Shape *last);
608 :
609 18555462 : uint32_t getObjectFlags() const { return base()->getObjectFlags(); }
610 79120927 : bool hasObjectFlag(BaseShape::Flag flag) const {
611 79120927 : JS_ASSERT(!(flag & ~BaseShape::OBJECT_FLAG_MASK));
612 79120927 : return !!(base()->flags & flag);
613 : }
614 :
615 : protected:
616 : /*
617 : * Implementation-private bits stored in shape->flags. See public: enum {}
618 : * flags further below, which were allocated FCFS over time, so interleave
619 : * with these bits.
620 : */
621 : enum {
622 : /* Property is placeholder for a non-native class. */
623 : NON_NATIVE = 0x01,
624 :
625 : /* Property stored in per-object dictionary, not shared property tree. */
626 : IN_DICTIONARY = 0x02,
627 :
628 : UNUSED_BITS = 0x3C
629 : };
630 :
631 : /* Get a shape identical to this one, without parent/kids information. */
632 : Shape(const StackShape &other, uint32_t nfixed);
633 :
634 : /* Used by EmptyShape (see jsscopeinlines.h). */
635 : Shape(UnownedBaseShape *base, uint32_t nfixed);
636 :
637 : /* Copy constructor disabled, to avoid misuse of the above form. */
638 : Shape(const Shape &other) MOZ_DELETE;
639 :
640 : /*
641 : * Whether this shape has a valid slot value. This may be true even if
642 : * !hasSlot() (see SlotInfo comment above), and may be false even if
643 : * hasSlot() if the shape is being constructed and has not had a slot
644 : * assigned yet. After construction, hasSlot() implies !hasMissingSlot().
645 : */
646 785484062 : bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
647 :
648 : public:
649 : /* Public bits stored in shape->flags. */
650 : enum {
651 : HAS_SHORTID = 0x40,
652 : PUBLIC_FLAGS = HAS_SHORTID
653 : };
654 :
655 1607752915 : bool inDictionary() const { return (flags & IN_DICTIONARY) != 0; }
656 0 : unsigned getFlags() const { return flags & PUBLIC_FLAGS; }
657 1881089 : bool hasShortID() const { return (flags & HAS_SHORTID) != 0; }
658 :
659 1979030 : PropertyOp getter() const { return base()->rawGetter; }
660 111199759 : bool hasDefaultGetter() const { return !base()->rawGetter; }
661 850304 : PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return base()->rawGetter; }
662 234 : JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return base()->getterObj; }
663 :
664 : // Per ES5, decode null getterObj as the undefined value, which encodes as null.
665 336897 : Value getterValue() const {
666 336897 : JS_ASSERT(hasGetterValue());
667 336897 : return base()->getterObj ? js::ObjectValue(*base()->getterObj) : js::UndefinedValue();
668 : }
669 :
670 9 : Value getterOrUndefined() const {
671 18 : return (hasGetterValue() && base()->getterObj)
672 9 : ? ObjectValue(*base()->getterObj)
673 27 : : UndefinedValue();
674 : }
675 :
676 1915226 : StrictPropertyOp setter() const { return base()->rawSetter; }
677 17416613 : bool hasDefaultSetter() const { return !base()->rawSetter; }
678 24591 : StrictPropertyOp setterOp() const { JS_ASSERT(!hasSetterValue()); return base()->rawSetter; }
679 63 : JSObject *setterObject() const { JS_ASSERT(hasSetterValue()); return base()->setterObj; }
680 :
681 : // Per ES5, decode null setterObj as the undefined value, which encodes as null.
682 9469 : Value setterValue() const {
683 9469 : JS_ASSERT(hasSetterValue());
684 9469 : return base()->setterObj ? js::ObjectValue(*base()->setterObj) : js::UndefinedValue();
685 : }
686 :
687 153 : Value setterOrUndefined() const {
688 297 : return (hasSetterValue() && base()->setterObj)
689 144 : ? ObjectValue(*base()->setterObj)
690 441 : : UndefinedValue();
691 : }
692 :
693 : void update(js::PropertyOp getter, js::StrictPropertyOp setter, uint8_t attrs);
694 :
695 : inline bool matches(const Shape *other) const;
696 : inline bool matches(const StackShape &other) const;
697 : inline bool matchesParamsAfterId(BaseShape *base,
698 : uint32_t aslot, unsigned aattrs, unsigned aflags,
699 : int ashortid) const;
700 :
701 : bool get(JSContext* cx, JSObject *receiver, JSObject *obj, JSObject *pobj, js::Value* vp) const;
702 : bool set(JSContext* cx, JSObject *obj, bool strict, js::Value* vp) const;
703 :
704 -1 : BaseShape *base() const { return base_; }
705 :
706 334287180 : bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
707 146260846 : uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); }
708 1376441725 : uint32_t maybeSlot() const { return slotInfo & SLOT_MASK; }
709 :
710 -1431668387 : bool isEmptyShape() const {
711 -1431668387 : JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
712 -1431668387 : return JSID_IS_EMPTY(propid_);
713 : }
714 :
715 555732078 : uint32_t slotSpan() const {
716 555732078 : JS_ASSERT(!inDictionary());
717 555732078 : uint32_t free = JSSLOT_FREE(getObjectClass());
718 555732078 : return hasMissingSlot() ? free : Max(free, maybeSlot() + 1);
719 : }
720 :
721 78790 : void setSlot(uint32_t slot) {
722 78790 : JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
723 78790 : slotInfo = slotInfo & ~Shape::SLOT_MASK;
724 78790 : slotInfo = slotInfo | slot;
725 78790 : }
726 :
727 65728214 : uint32_t numFixedSlots() const {
728 65728214 : return (slotInfo >> FIXED_SLOTS_SHIFT);
729 : }
730 :
731 355342 : void setNumFixedSlots(uint32_t nfixed) {
732 355342 : JS_ASSERT(nfixed < FIXED_SLOTS_MAX);
733 355342 : slotInfo = slotInfo & ~FIXED_SLOTS_MASK;
734 355342 : slotInfo = slotInfo | (nfixed << FIXED_SLOTS_SHIFT);
735 355342 : }
736 :
737 62610341 : uint32_t numLinearSearches() const {
738 62610341 : return (slotInfo & LINEAR_SEARCHES_MASK) >> LINEAR_SEARCHES_SHIFT;
739 : }
740 :
741 14201164 : void incrementNumLinearSearches() {
742 14201164 : uint32_t count = numLinearSearches();
743 14201164 : JS_ASSERT(count < LINEAR_SEARCHES_MAX);
744 14201164 : slotInfo = slotInfo & ~LINEAR_SEARCHES_MASK;
745 14201164 : slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT);
746 14201164 : }
747 :
748 182917276 : const HeapId &propid() const {
749 182917276 : JS_ASSERT(!isEmptyShape());
750 182917276 : JS_ASSERT(!JSID_IS_VOID(propid_));
751 182917276 : return propid_;
752 : }
753 901648177 : HeapId &propidRef() { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
754 :
755 969161 : int16_t shortid() const { JS_ASSERT(hasShortID()); return maybeShortid(); }
756 1001283 : int16_t maybeShortid() const { return shortid_; }
757 :
758 : /*
759 : * If SHORTID is set in shape->flags, we use shape->shortid rather
760 : * than id when calling shape's getter or setter.
761 : */
762 861692 : jsid getUserId() const {
763 861692 : return hasShortID() ? INT_TO_JSID(shortid()) : propid();
764 : }
765 :
766 862795 : uint8_t attributes() const { return attrs; }
767 2246098 : bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; }
768 34360340 : bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; }
769 16218453 : bool writable() const {
770 : // JS_ASSERT(isDataDescriptor());
771 16218453 : return (attrs & JSPROP_READONLY) == 0;
772 : }
773 2425493 : bool hasGetterValue() const { return attrs & JSPROP_GETTER; }
774 90922 : bool hasSetterValue() const { return attrs & JSPROP_SETTER; }
775 :
776 93332318 : bool isDataDescriptor() const {
777 93332318 : return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
778 : }
779 766167 : bool isAccessorDescriptor() const {
780 766167 : return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
781 : }
782 :
783 : /*
784 : * For ES5 compatibility, we allow properties with PropertyOp-flavored
785 : * setters to be shadowed when set. The "own" property thereby created in
786 : * the directly referenced object will have the same getter and setter as
787 : * the prototype property. See bug 552432.
788 : */
789 503601 : bool shadowable() const {
790 503601 : JS_ASSERT_IF(isDataDescriptor(), writable());
791 503601 : return hasSlot() || (attrs & JSPROP_SHADOWABLE);
792 : }
793 :
794 : /*
795 : * Sometimes call objects and run-time block objects need unique shapes, but
796 : * sometimes they don't.
797 : *
798 : * Property cache entries only record the shapes of the first and last
799 : * objects along the search path, so if the search traverses more than those
800 : * two objects, then those first and last shapes must determine the shapes
801 : * of everything else along the path. The js_PurgeScopeChain stuff takes
802 : * care of making this work, but that suffices only because we require that
803 : * start points with the same shape have the same successor object in the
804 : * search path --- a cache hit means the starting shapes were equal, which
805 : * means the seach path tail (everything but the first object in the path)
806 : * was shared, which in turn means the effects of a purge will be seen by
807 : * all affected starting search points.
808 : *
809 : * For call and run-time block objects, the "successor object" is the scope
810 : * chain parent. Unlike prototype objects (of which there are usually few),
811 : * scope chain parents are created frequently (possibly on every call), so
812 : * following the shape-implies-parent rule blindly would lead one to give
813 : * every call and block its own shape.
814 : *
815 : * In many cases, however, it's not actually necessary to give call and
816 : * block objects their own shapes, and we can do better. If the code will
817 : * always be used with the same global object, and none of the enclosing
818 : * call objects could have bindings added to them at runtime (by direct eval
819 : * calls or function statements), then we can use a fixed set of shapes for
820 : * those objects. You could think of the shapes in the functions' bindings
821 : * and compile-time blocks as uniquely identifying the global object(s) at
822 : * the end of the scope chain.
823 : *
824 : * (In fact, some JSScripts we do use against multiple global objects (see
825 : * bug 618497), and using the fixed shapes isn't sound there.)
826 : *
827 : * In deciding whether a call or block has any extensible parents, we
828 : * actually only need to consider enclosing calls; blocks are never
829 : * extensible, and the other sorts of objects that appear in the scope
830 : * chains ('with' blocks, say) are not CacheableNonGlobalScopes.
831 : *
832 : * If the hasExtensibleParents flag is set for the last property in a
833 : * script's bindings or a compiler-generated Block object, then created
834 : * Call or Block objects need unique shapes. If the flag is clear, then we
835 : * can use lastBinding's shape.
836 : */
837 : static Shape *setExtensibleParents(JSContext *cx, Shape *shape);
838 818526 : bool extensibleParents() const { return !!(base()->flags & BaseShape::EXTENSIBLE_PARENTS); }
839 :
840 29833330 : uint32_t entryCount() const {
841 29833330 : if (hasTable())
842 760476 : return table().entryCount;
843 :
844 29072854 : const js::Shape *shape = this;
845 29072854 : uint32_t count = 0;
846 1217439101 : for (js::Shape::Range r = shape->all(); !r.empty(); r.popFront())
847 1188366247 : ++count;
848 29072854 : return count;
849 : }
850 :
851 34208013 : bool isBigEnoughForAPropertyTable() const {
852 34208013 : JS_ASSERT(!hasTable());
853 34208013 : const js::Shape *shape = this;
854 34208013 : uint32_t count = 0;
855 88017809 : for (js::Shape::Range r = shape->all(); !r.empty(); r.popFront()) {
856 53817291 : ++count;
857 53817291 : if (count >= PropertyTable::MIN_ENTRIES)
858 7495 : return true;
859 : }
860 34200518 : return false;
861 : }
862 :
863 : #ifdef DEBUG
864 : void dump(JSContext *cx, FILE *fp) const;
865 : void dumpSubtree(JSContext *cx, int level, FILE *fp) const;
866 : #endif
867 :
868 : void finalize(FreeOp *fop);
869 : void removeChild(js::Shape *child);
870 :
871 : static inline void writeBarrierPre(const Shape *shape);
872 : static inline void writeBarrierPost(const Shape *shape, void *addr);
873 :
874 : /*
875 : * All weak references need a read barrier for incremental GC. This getter
876 : * method implements the read barrier. It's used to obtain initial shapes
877 : * from the compartment.
878 : */
879 : static inline void readBarrier(const Shape *shape);
880 :
881 : static inline ThingRootKind rootKind() { return THING_ROOT_SHAPE; }
882 :
883 : inline void markChildren(JSTracer *trc);
884 :
885 : /* For JIT usage */
886 116121 : static inline size_t offsetOfBase() { return offsetof(Shape, base_); }
887 :
888 : private:
889 : static void staticAsserts() {
890 : JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base));
891 : JS_STATIC_ASSERT(offsetof(Shape, slotInfo) == offsetof(js::shadow::Shape, slotInfo));
892 : JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Shape::FIXED_SLOTS_SHIFT);
893 : }
894 : };
895 :
896 : struct EmptyShape : public js::Shape
897 : {
898 : EmptyShape(UnownedBaseShape *base, uint32_t nfixed);
899 :
900 : /*
901 : * Lookup an initial shape matching the given parameters, creating an empty
902 : * shape if none was found.
903 : */
904 : static Shape *getInitialShape(JSContext *cx, Class *clasp, JSObject *proto,
905 : JSObject *parent, gc::AllocKind kind, uint32_t objectFlags = 0);
906 :
907 : /*
908 : * Reinsert an alternate initial shape, to be returned by future
909 : * getInitialShape calls, until the new shape becomes unreachable in a GC
910 : * and the table entry is purged.
911 : */
912 : static void insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto);
913 : };
914 :
915 : /*
916 : * Entries for the per-compartment initialShapes set indexing initial shapes
917 : * for objects in the compartment and the associated types.
918 : */
919 : struct InitialShapeEntry
920 10782720 : {
921 : /*
922 : * Initial shape to give to the object. This is an empty shape, except for
923 : * certain classes (e.g. String, RegExp) which may add certain baked-in
924 : * properties.
925 : */
926 : ReadBarriered<Shape> shape;
927 :
928 : /*
929 : * Matching prototype for the entry. The shape of an object determines its
930 : * prototype, but the prototype cannot be determined from the shape itself.
931 : */
932 : JSObject *proto;
933 :
934 : /* State used to determine a match on an initial shape. */
935 : struct Lookup {
936 : Class *clasp;
937 : JSObject *proto;
938 : JSObject *parent;
939 : uint32_t nfixed;
940 : uint32_t baseFlags;
941 8611900 : Lookup(Class *clasp, JSObject *proto, JSObject *parent, uint32_t nfixed,
942 : uint32_t baseFlags)
943 : : clasp(clasp), proto(proto), parent(parent),
944 8611900 : nfixed(nfixed), baseFlags(baseFlags)
945 8611900 : {}
946 : };
947 :
948 : static inline HashNumber hash(const Lookup &lookup);
949 : static inline bool match(const InitialShapeEntry &key, const Lookup &lookup);
950 : };
951 :
952 : typedef HashSet<InitialShapeEntry, InitialShapeEntry, SystemAllocPolicy> InitialShapeSet;
953 :
954 : struct StackShape
955 : {
956 : UnownedBaseShape *base;
957 : jsid propid;
958 : uint32_t slot_;
959 : uint8_t attrs;
960 : uint8_t flags;
961 : int16_t shortid;
962 :
963 30465010 : StackShape(UnownedBaseShape *base, jsid propid, uint32_t slot,
964 : uint32_t nfixed, unsigned attrs, unsigned flags, int shortid)
965 : : base(base),
966 : propid(propid),
967 : slot_(slot),
968 : attrs(uint8_t(attrs)),
969 : flags(uint8_t(flags)),
970 30465010 : shortid(int16_t(shortid))
971 : {
972 30465010 : JS_ASSERT(base);
973 30465010 : JS_ASSERT(!JSID_IS_VOID(propid));
974 30465010 : JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
975 30465010 : }
976 :
977 9342637 : StackShape(const Shape *shape)
978 9342637 : : base(shape->base()->unowned()),
979 9342637 : propid(const_cast<Shape *>(shape)->propidRef()),
980 : slot_(shape->slotInfo & Shape::SLOT_MASK),
981 : attrs(shape->attrs),
982 : flags(shape->flags),
983 18685274 : shortid(shape->shortid_)
984 9342637 : {}
985 :
986 33407705 : bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
987 20594627 : bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
988 :
989 1634797 : uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; }
990 39239170 : uint32_t maybeSlot() const { return slot_; }
991 :
992 355342 : uint32_t slotSpan() const {
993 355342 : uint32_t free = JSSLOT_FREE(base->clasp);
994 355342 : return hasMissingSlot() ? free : (maybeSlot() + 1);
995 : }
996 :
997 29635563 : void setSlot(uint32_t slot) {
998 29635563 : JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
999 29635563 : slot_ = slot;
1000 29635563 : }
1001 :
1002 : inline HashNumber hash() const;
1003 : };
1004 :
1005 : /* Rooter for stack allocated shapes. */
1006 : class RootStackShape
1007 15162673 : {
1008 : Root<const UnownedBaseShape*> baseShapeRoot;
1009 : Root<const jsid> propidRoot;
1010 :
1011 : public:
1012 15162673 : RootStackShape(JSContext *cx, const StackShape *shape)
1013 : : baseShapeRoot(cx, &shape->base),
1014 15162673 : propidRoot(cx, &shape->propid)
1015 15162673 : {}
1016 : };
1017 :
1018 : } /* namespace js */
1019 :
1020 : /* js::Shape pointer tag bit indicating a collision. */
1021 : #define SHAPE_COLLISION (uintptr_t(1))
1022 : #define SHAPE_REMOVED ((js::Shape *) SHAPE_COLLISION)
1023 :
1024 : /* Macros to get and set shape pointer values and collision flags. */
1025 : #define SHAPE_IS_FREE(shape) ((shape) == NULL)
1026 : #define SHAPE_IS_REMOVED(shape) ((shape) == SHAPE_REMOVED)
1027 : #define SHAPE_IS_LIVE(shape) ((shape) > SHAPE_REMOVED)
1028 : #define SHAPE_FLAG_COLLISION(spp,shape) (*(spp) = (js::Shape *) \
1029 : (uintptr_t(shape) | SHAPE_COLLISION))
1030 : #define SHAPE_HAD_COLLISION(shape) (uintptr_t(shape) & SHAPE_COLLISION)
1031 : #define SHAPE_FETCH(spp) SHAPE_CLEAR_COLLISION(*(spp))
1032 :
1033 : #define SHAPE_CLEAR_COLLISION(shape) \
1034 : ((js::Shape *) (uintptr_t(shape) & ~SHAPE_COLLISION))
1035 :
1036 : #define SHAPE_STORE_PRESERVING_COLLISION(spp, shape) \
1037 : (*(spp) = (js::Shape *) (uintptr_t(shape) | SHAPE_HAD_COLLISION(*(spp))))
1038 :
1039 : namespace js {
1040 :
1041 : inline Shape *
1042 98304497 : Shape::search(JSContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding)
1043 : {
1044 98304497 : if (start->inDictionary()) {
1045 40652699 : *pspp = start->table().search(id, adding);
1046 40652699 : return SHAPE_FETCH(*pspp);
1047 : }
1048 :
1049 57651798 : *pspp = NULL;
1050 :
1051 57651798 : if (start->hasTable()) {
1052 9242621 : Shape **spp = start->table().search(id, adding);
1053 9242621 : return SHAPE_FETCH(spp);
1054 : }
1055 :
1056 48409177 : if (start->numLinearSearches() == LINEAR_SEARCHES_MAX) {
1057 34208013 : if (start->isBigEnoughForAPropertyTable()) {
1058 14990 : RootShape startRoot(cx, &start);
1059 14990 : RootId idRoot(cx, &id);
1060 7495 : if (start->hashify(cx)) {
1061 7495 : Shape **spp = start->table().search(id, adding);
1062 7495 : return SHAPE_FETCH(spp);
1063 : }
1064 : }
1065 : /*
1066 : * No table built -- there weren't enough entries, or OOM occurred.
1067 : * Don't increment numLinearSearches, to keep hasTable() false.
1068 : */
1069 34200518 : JS_ASSERT(!start->hasTable());
1070 : } else {
1071 14201164 : start->incrementNumLinearSearches();
1072 : }
1073 :
1074 905401775 : for (Shape *shape = start; shape; shape = shape->parent) {
1075 869602028 : if (shape->propidRef() == id)
1076 12601935 : return shape;
1077 : }
1078 :
1079 35799747 : return NULL;
1080 : }
1081 :
1082 : } // namespace js
1083 :
1084 : #ifdef _MSC_VER
1085 : #pragma warning(pop)
1086 : #pragma warning(pop)
1087 : #endif
1088 :
1089 : namespace JS {
1090 : template<> class AnchorPermitted<js::Shape *> { };
1091 : template<> class AnchorPermitted<const js::Shape *> { };
1092 : }
1093 :
1094 : #endif /* jsscope_h___ */
|