Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef vm_Shape_h
8 #define vm_Shape_h
10 #include "mozilla/Attributes.h"
11 #include "mozilla/GuardObjects.h"
12 #include "mozilla/MathAlgorithms.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/MemoryReporting.h"
15 #include "mozilla/TemplateLib.h"
17 #include "jsapi.h"
18 #include "jsfriendapi.h"
19 #include "jsinfer.h"
20 #include "jspropertytree.h"
21 #include "jstypes.h"
22 #include "NamespaceImports.h"
24 #include "gc/Barrier.h"
25 #include "gc/Heap.h"
26 #include "gc/Marking.h"
27 #include "gc/Rooting.h"
28 #include "js/HashTable.h"
29 #include "js/RootingAPI.h"
31 #ifdef _MSC_VER
32 #pragma warning(push)
33 #pragma warning(disable:4800)
34 #pragma warning(push)
35 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
36 #endif
38 /*
39 * In isolation, a Shape represents a property that exists in one or more
40 * objects; it has an id, flags, etc. (But it doesn't represent the property's
41 * value.) However, Shapes are always stored in linked linear sequence of
42 * Shapes, called "shape lineages". Each shape lineage represents the layout of
43 * an entire object.
44 *
45 * Every JSObject has a pointer, |shape_|, accessible via lastProperty(), to
46 * the last Shape in a shape lineage, which identifies the property most
47 * recently added to the object. This pointer permits fast object layout
48 * tests. The shape lineage order also dictates the enumeration order for the
49 * object; ECMA requires no particular order but this implementation has
50 * promised and delivered property definition order.
51 *
52 * Shape lineages occur in two kinds of data structure.
53 *
54 * 1. N-ary property trees. Each path from a non-root node to the root node in
55 * a property tree is a shape lineage. Property trees permit full (or
56 * partial) sharing of Shapes between objects that have fully (or partly)
57 * identical layouts. The root is an EmptyShape whose identity is determined
58 * by the object's class, compartment and prototype. These Shapes are shared
59 * and immutable.
60 *
61 * 2. Dictionary mode lists. Shapes in such lists are said to be "in
62 * dictionary mode", as are objects that point to such Shapes. These Shapes
63 * are unshared, private to a single object, and immutable except for their
64 * links in the dictionary list.
65 *
66 * All shape lineages are bi-directionally linked, via the |parent| and
67 * |kids|/|listp| members.
68 *
69 * Shape lineages start out life in the property tree. They can be converted
70 * (by copying) to dictionary mode lists in the following circumstances.
71 *
72 * 1. The shape lineage's size reaches MAX_HEIGHT. This reasonable limit avoids
73 * potential worst cases involving shape lineage mutations.
74 *
75 * 2. A property represented by a non-last Shape in a shape lineage is removed
76 * from an object. (In the last Shape case, obj->shape_ can be easily
77 * adjusted to point to obj->shape_->parent.) We originally tried lazy
78 * forking of the property tree, but this blows up for delete/add
79 * repetitions.
80 *
81 * 3. A property represented by a non-last Shape in a shape lineage has its
82 * attributes modified.
83 *
84 * To find the Shape for a particular property of an object initially requires
85 * a linear search. But if the number of searches starting at any particular
86 * Shape in the property tree exceeds MAX_LINEAR_SEARCHES and the Shape's
87 * lineage has (excluding the EmptyShape) at least MIN_ENTRIES, we create an
88 * auxiliary hash table -- the ShapeTable -- that allows faster lookup.
89 * Furthermore, a ShapeTable is always created for dictionary mode lists,
90 * and it is attached to the last Shape in the lineage. Shape tables for
91 * property tree Shapes never change, but shape tables for dictionary mode
92 * Shapes can grow and shrink.
93 *
94 * There used to be a long, math-heavy comment here explaining why property
95 * trees are more space-efficient than alternatives. This was removed in bug
96 * 631138; see that bug for the full details.
97 *
98 * Because many Shapes have similar data, there is actually a secondary type
99 * called a BaseShape that holds some of a Shape's data. Many shapes can share
100 * a single BaseShape.
101 */
103 #define JSSLOT_FREE(clasp) JSCLASS_RESERVED_SLOTS(clasp)
105 namespace js {
107 class Bindings;
108 class Debugger;
109 class Nursery;
110 class ObjectImpl;
111 class StaticBlockObject;
113 typedef JSPropertyOp PropertyOp;
114 typedef JSStrictPropertyOp StrictPropertyOp;
115 typedef JSPropertyDescriptor PropertyDescriptor;
117 /* Limit on the number of slotful properties in an object. */
118 static const uint32_t SHAPE_INVALID_SLOT = JS_BIT(24) - 1;
119 static const uint32_t SHAPE_MAXIMUM_SLOT = JS_BIT(24) - 2;
121 static inline PropertyOp
122 CastAsPropertyOp(JSObject *object)
123 {
124 return JS_DATA_TO_FUNC_PTR(PropertyOp, object);
125 }
127 static inline StrictPropertyOp
128 CastAsStrictPropertyOp(JSObject *object)
129 {
130 return JS_DATA_TO_FUNC_PTR(StrictPropertyOp, object);
131 }
133 /*
134 * A representation of ECMA-262 ed. 5's internal Property Descriptor data
135 * structure.
136 */
137 struct PropDesc {
138 private:
139 /*
140 * Original object from which this descriptor derives, passed through for
141 * the benefit of proxies. FIXME: Remove this when direct proxies happen.
142 */
143 Value pd_;
145 Value value_, get_, set_;
147 /* Property descriptor boolean fields. */
148 uint8_t attrs;
150 /* Bits indicating which values are set. */
151 bool hasGet_ : 1;
152 bool hasSet_ : 1;
153 bool hasValue_ : 1;
154 bool hasWritable_ : 1;
155 bool hasEnumerable_ : 1;
156 bool hasConfigurable_ : 1;
158 /* Or maybe this represents a property's absence, and it's undefined. */
159 bool isUndefined_ : 1;
161 PropDesc(const Value &v)
162 : pd_(UndefinedValue()),
163 value_(v),
164 get_(UndefinedValue()), set_(UndefinedValue()),
165 attrs(0),
166 hasGet_(false), hasSet_(false),
167 hasValue_(true), hasWritable_(false), hasEnumerable_(false), hasConfigurable_(false),
168 isUndefined_(false)
169 {
170 }
172 public:
173 friend class AutoPropDescRooter;
174 friend void JS::AutoGCRooter::trace(JSTracer *trc);
176 enum Enumerability { Enumerable = true, NonEnumerable = false };
177 enum Configurability { Configurable = true, NonConfigurable = false };
178 enum Writability { Writable = true, NonWritable = false };
180 PropDesc();
182 static PropDesc undefined() { return PropDesc(); }
183 static PropDesc valueOnly(const Value &v) { return PropDesc(v); }
185 PropDesc(const Value &v, Writability writable,
186 Enumerability enumerable, Configurability configurable)
187 : pd_(UndefinedValue()),
188 value_(v),
189 get_(UndefinedValue()), set_(UndefinedValue()),
190 attrs((writable ? 0 : JSPROP_READONLY) |
191 (enumerable ? JSPROP_ENUMERATE : 0) |
192 (configurable ? 0 : JSPROP_PERMANENT)),
193 hasGet_(false), hasSet_(false),
194 hasValue_(true), hasWritable_(true), hasEnumerable_(true), hasConfigurable_(true),
195 isUndefined_(false)
196 {}
198 inline PropDesc(const Value &getter, const Value &setter,
199 Enumerability enumerable, Configurability configurable);
201 /*
202 * 8.10.5 ToPropertyDescriptor(Obj)
203 *
204 * If checkAccessors is false, skip steps 7.b and 8.b, which throw a
205 * TypeError if .get or .set is neither a callable object nor undefined.
206 *
207 * (DebuggerObject_defineProperty uses this: the .get and .set properties
208 * are expected to be Debugger.Object wrappers of functions, which are not
209 * themselves callable.)
210 */
211 bool initialize(JSContext *cx, const Value &v, bool checkAccessors = true);
213 /*
214 * If IsGenericDescriptor(desc) or IsDataDescriptor(desc) is true, then if
215 * the value of an attribute field of desc, considered as a data
216 * descriptor, is absent, set it to its default value. Else if the value of
217 * an attribute field of desc, considered as an attribute descriptor, is
218 * absent, set it to its default value.
219 */
220 void complete();
222 /*
223 * 8.10.4 FromPropertyDescriptor(Desc)
224 *
225 * initFromPropertyDescriptor sets pd to undefined and populates all the
226 * other fields of this PropDesc from desc.
227 *
228 * makeObject populates pd based on the other fields of *this, creating a
229 * new property descriptor JSObject and defining properties on it.
230 */
231 void initFromPropertyDescriptor(Handle<PropertyDescriptor> desc);
232 bool makeObject(JSContext *cx);
234 void setUndefined() { isUndefined_ = true; }
236 bool isUndefined() const { return isUndefined_; }
238 bool hasGet() const { MOZ_ASSERT(!isUndefined()); return hasGet_; }
239 bool hasSet() const { MOZ_ASSERT(!isUndefined()); return hasSet_; }
240 bool hasValue() const { MOZ_ASSERT(!isUndefined()); return hasValue_; }
241 bool hasWritable() const { MOZ_ASSERT(!isUndefined()); return hasWritable_; }
242 bool hasEnumerable() const { MOZ_ASSERT(!isUndefined()); return hasEnumerable_; }
243 bool hasConfigurable() const { MOZ_ASSERT(!isUndefined()); return hasConfigurable_; }
245 Value pd() const { MOZ_ASSERT(!isUndefined()); return pd_; }
246 void clearPd() { pd_ = UndefinedValue(); }
248 uint8_t attributes() const { MOZ_ASSERT(!isUndefined()); return attrs; }
250 /* 8.10.1 IsAccessorDescriptor(desc) */
251 bool isAccessorDescriptor() const {
252 return !isUndefined() && (hasGet() || hasSet());
253 }
255 /* 8.10.2 IsDataDescriptor(desc) */
256 bool isDataDescriptor() const {
257 return !isUndefined() && (hasValue() || hasWritable());
258 }
260 /* 8.10.3 IsGenericDescriptor(desc) */
261 bool isGenericDescriptor() const {
262 return !isUndefined() && !isAccessorDescriptor() && !isDataDescriptor();
263 }
265 bool configurable() const {
266 MOZ_ASSERT(!isUndefined());
267 MOZ_ASSERT(hasConfigurable());
268 return (attrs & JSPROP_PERMANENT) == 0;
269 }
271 bool enumerable() const {
272 MOZ_ASSERT(!isUndefined());
273 MOZ_ASSERT(hasEnumerable());
274 return (attrs & JSPROP_ENUMERATE) != 0;
275 }
277 bool writable() const {
278 MOZ_ASSERT(!isUndefined());
279 MOZ_ASSERT(hasWritable());
280 return (attrs & JSPROP_READONLY) == 0;
281 }
283 HandleValue value() const {
284 MOZ_ASSERT(hasValue());
285 return HandleValue::fromMarkedLocation(&value_);
286 }
288 JSObject * getterObject() const {
289 MOZ_ASSERT(!isUndefined());
290 MOZ_ASSERT(hasGet());
291 return get_.isUndefined() ? nullptr : &get_.toObject();
292 }
293 JSObject * setterObject() const {
294 MOZ_ASSERT(!isUndefined());
295 MOZ_ASSERT(hasSet());
296 return set_.isUndefined() ? nullptr : &set_.toObject();
297 }
299 HandleValue getterValue() const {
300 MOZ_ASSERT(!isUndefined());
301 MOZ_ASSERT(hasGet());
302 return HandleValue::fromMarkedLocation(&get_);
303 }
304 HandleValue setterValue() const {
305 MOZ_ASSERT(!isUndefined());
306 MOZ_ASSERT(hasSet());
307 return HandleValue::fromMarkedLocation(&set_);
308 }
310 /*
311 * Unfortunately the values produced by these methods are used such that
312 * we can't assert anything here. :-(
313 */
314 PropertyOp getter() const {
315 return CastAsPropertyOp(get_.isUndefined() ? nullptr : &get_.toObject());
316 }
317 StrictPropertyOp setter() const {
318 return CastAsStrictPropertyOp(set_.isUndefined() ? nullptr : &set_.toObject());
319 }
321 /*
322 * Throw a TypeError if a getter/setter is present and is neither callable
323 * nor undefined. These methods do exactly the type checks that are skipped
324 * by passing false as the checkAccessors parameter of initialize.
325 */
326 bool checkGetter(JSContext *cx);
327 bool checkSetter(JSContext *cx);
329 bool unwrapDebuggerObjectsInto(JSContext *cx, Debugger *dbg, HandleObject obj,
330 PropDesc *unwrapped) const;
332 bool wrapInto(JSContext *cx, HandleObject obj, const jsid &id, jsid *wrappedId,
333 PropDesc *wrappedDesc) const;
334 };
336 class AutoPropDescRooter : private JS::CustomAutoRooter
337 {
338 public:
339 explicit AutoPropDescRooter(JSContext *cx
340 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
341 : CustomAutoRooter(cx)
342 {
343 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
344 }
346 PropDesc& getPropDesc() { return propDesc; }
348 void initFromPropertyDescriptor(Handle<PropertyDescriptor> desc) {
349 propDesc.initFromPropertyDescriptor(desc);
350 }
352 bool makeObject(JSContext *cx) {
353 return propDesc.makeObject(cx);
354 }
356 void setUndefined() { propDesc.setUndefined(); }
357 bool isUndefined() const { return propDesc.isUndefined(); }
359 bool hasGet() const { return propDesc.hasGet(); }
360 bool hasSet() const { return propDesc.hasSet(); }
361 bool hasValue() const { return propDesc.hasValue(); }
362 bool hasWritable() const { return propDesc.hasWritable(); }
363 bool hasEnumerable() const { return propDesc.hasEnumerable(); }
364 bool hasConfigurable() const { return propDesc.hasConfigurable(); }
366 Value pd() const { return propDesc.pd(); }
367 void clearPd() { propDesc.clearPd(); }
369 uint8_t attributes() const { return propDesc.attributes(); }
371 bool isAccessorDescriptor() const { return propDesc.isAccessorDescriptor(); }
372 bool isDataDescriptor() const { return propDesc.isDataDescriptor(); }
373 bool isGenericDescriptor() const { return propDesc.isGenericDescriptor(); }
374 bool configurable() const { return propDesc.configurable(); }
375 bool enumerable() const { return propDesc.enumerable(); }
376 bool writable() const { return propDesc.writable(); }
378 HandleValue value() const { return propDesc.value(); }
379 JSObject *getterObject() const { return propDesc.getterObject(); }
380 JSObject *setterObject() const { return propDesc.setterObject(); }
381 HandleValue getterValue() const { return propDesc.getterValue(); }
382 HandleValue setterValue() const { return propDesc.setterValue(); }
384 PropertyOp getter() const { return propDesc.getter(); }
385 StrictPropertyOp setter() const { return propDesc.setter(); }
387 private:
388 virtual void trace(JSTracer *trc);
390 PropDesc propDesc;
391 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
392 };
394 /*
395 * Shapes use multiplicative hashing, but specialized to
396 * minimize footprint.
397 */
398 struct ShapeTable {
399 static const uint32_t HASH_BITS = mozilla::tl::BitSize<HashNumber>::value;
400 static const uint32_t MIN_ENTRIES = 7;
401 static const uint32_t MIN_SIZE_LOG2 = 4;
402 static const uint32_t MIN_SIZE = JS_BIT(MIN_SIZE_LOG2);
404 int hashShift; /* multiplicative hash shift */
406 uint32_t entryCount; /* number of entries in table */
407 uint32_t removedCount; /* removed entry sentinels in table */
408 uint32_t freelist; /* SHAPE_INVALID_SLOT or head of slot
409 freelist in owning dictionary-mode
410 object */
411 js::Shape **entries; /* table of ptrs to shared tree nodes */
413 ShapeTable(uint32_t nentries)
414 : hashShift(HASH_BITS - MIN_SIZE_LOG2),
415 entryCount(nentries),
416 removedCount(0),
417 freelist(SHAPE_INVALID_SLOT)
418 {
419 /* NB: entries is set by init, which must be called. */
420 }
422 ~ShapeTable() {
423 js_free(entries);
424 }
426 /* By definition, hashShift = HASH_BITS - log2(capacity). */
427 uint32_t capacity() const { return JS_BIT(HASH_BITS - hashShift); }
429 /* Computes the size of the entries array for a given capacity. */
430 static size_t sizeOfEntries(size_t cap) { return cap * sizeof(Shape *); }
432 /*
433 * This counts the ShapeTable object itself (which must be
434 * heap-allocated) and its |entries| array.
435 */
436 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
437 return mallocSizeOf(this) + mallocSizeOf(entries);
438 }
440 /* Whether we need to grow. We want to do this if the load factor is >= 0.75 */
441 bool needsToGrow() const {
442 uint32_t size = capacity();
443 return entryCount + removedCount >= size - (size >> 2);
444 }
446 /*
447 * Try to grow the table. On failure, reports out of memory on cx
448 * and returns false. This will make any extant pointers into the
449 * table invalid. Don't call this unless needsToGrow() is true.
450 */
451 bool grow(ThreadSafeContext *cx);
453 /*
454 * NB: init and change are fallible but do not report OOM, so callers can
455 * cope or ignore. They do however use the context's calloc_ method in
456 * order to update the malloc counter on success.
457 */
458 bool init(ThreadSafeContext *cx, Shape *lastProp);
459 bool change(int log2Delta, ThreadSafeContext *cx);
460 Shape **search(jsid id, bool adding);
461 };
463 /*
464 * Reuse the API-only JSPROP_INDEX attribute to mean shadowability.
465 */
466 #define JSPROP_SHADOWABLE JSPROP_INDEX
468 /*
469 * Shapes encode information about both a property lineage *and* a particular
470 * property. This information is split across the Shape and the BaseShape
471 * at shape->base(). Both Shape and BaseShape can be either owned or unowned
472 * by, respectively, the Object or Shape referring to them.
473 *
474 * Owned Shapes are used in dictionary objects, and form a doubly linked list
475 * whose entries are all owned by that dictionary. Unowned Shapes are all in
476 * the property tree.
477 *
478 * Owned BaseShapes are used for shapes which have shape tables, including
479 * the last properties in all dictionaries. Unowned BaseShapes compactly store
480 * information common to many shapes. In a given compartment there is a single
481 * BaseShape for each combination of BaseShape information. This information
482 * is cloned in owned BaseShapes so that information can be quickly looked up
483 * for a given object or shape without regard to whether the base shape is
484 * owned or not.
485 *
486 * All combinations of owned/unowned Shapes/BaseShapes are possible:
487 *
488 * Owned Shape, Owned BaseShape:
489 *
490 * Last property in a dictionary object. The BaseShape is transferred from
491 * property to property as the object's last property changes.
492 *
493 * Owned Shape, Unowned BaseShape:
494 *
495 * Property in a dictionary object other than the last one.
496 *
497 * Unowned Shape, Owned BaseShape:
498 *
499 * Property in the property tree which has a shape table.
500 *
501 * Unowned Shape, Unowned BaseShape:
502 *
503 * Property in the property tree which does not have a shape table.
504 *
505 * BaseShapes additionally encode some information about the referring object
506 * itself. This includes the object's class, parent and various flags that may
507 * be set for the object. Except for the class, this information is mutable and
508 * may change when the object has an established property lineage. On such
509 * changes the entire property lineage is not updated, but rather only the
510 * last property (and its base shape). This works because only the object's
511 * last property is used to query information about the object. Care must be
512 * taken to call JSObject::canRemoveLastProperty when unwinding an object to
513 * an earlier property, however.
514 */
516 class Shape;
517 class UnownedBaseShape;
518 struct StackBaseShape;
520 namespace gc {
521 void MergeCompartments(JSCompartment *source, JSCompartment *target);
522 }
524 static inline void
525 GetterSetterWriteBarrierPost(JSRuntime *rt, JSObject **objp)
526 {
527 #ifdef JSGC_GENERATIONAL
528 JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
529 shadowRuntime->gcStoreBufferPtr()->putRelocatableCell(reinterpret_cast<gc::Cell **>(objp));
530 #endif
531 }
533 static inline void
534 GetterSetterWriteBarrierPostRemove(JSRuntime *rt, JSObject **objp)
535 {
536 #ifdef JSGC_GENERATIONAL
537 JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
538 shadowRuntime->gcStoreBufferPtr()->removeRelocatableCell(reinterpret_cast<gc::Cell **>(objp));
539 #endif
540 }
542 class BaseShape : public gc::BarrieredCell<BaseShape>
543 {
544 public:
545 friend class Shape;
546 friend struct StackBaseShape;
547 friend struct StackShape;
548 friend void gc::MergeCompartments(JSCompartment *source, JSCompartment *target);
550 enum Flag {
551 /* Owned by the referring shape. */
552 OWNED_SHAPE = 0x1,
554 /* getterObj/setterObj are active in unions below. */
555 HAS_GETTER_OBJECT = 0x2,
556 HAS_SETTER_OBJECT = 0x4,
558 /*
559 * Flags set which describe the referring object. Once set these cannot
560 * be unset (except during object densification of sparse indexes), and
561 * are transferred from shape to shape as the object's last property
562 * changes.
563 *
564 * If you add a new flag here, please add appropriate code to
565 * JSObject::dump to dump it as part of object representation.
566 */
568 DELEGATE = 0x8,
569 NOT_EXTENSIBLE = 0x10,
570 INDEXED = 0x20,
571 BOUND_FUNCTION = 0x40,
572 VAROBJ = 0x80,
573 WATCHED = 0x100,
574 ITERATED_SINGLETON = 0x200,
575 NEW_TYPE_UNKNOWN = 0x400,
576 UNCACHEABLE_PROTO = 0x800,
577 HAD_ELEMENTS_ACCESS = 0x1000,
579 OBJECT_FLAG_MASK = 0x1ff8
580 };
582 private:
583 const Class *clasp_; /* Class of referring object. */
584 HeapPtrObject parent; /* Parent of referring object. */
585 HeapPtrObject metadata; /* Optional holder of metadata about
586 * the referring object. */
587 JSCompartment *compartment_; /* Compartment shape belongs to. */
588 uint32_t flags; /* Vector of above flags. */
589 uint32_t slotSpan_; /* Object slot span for BaseShapes at
590 * dictionary last properties. */
592 union {
593 PropertyOp rawGetter; /* getter hook for shape */
594 JSObject *getterObj; /* user-defined callable "get" object or
595 null if shape->hasGetterValue() */
596 };
598 union {
599 StrictPropertyOp rawSetter; /* setter hook for shape */
600 JSObject *setterObj; /* user-defined callable "set" object or
601 null if shape->hasSetterValue() */
602 };
604 /* For owned BaseShapes, the canonical unowned BaseShape. */
605 HeapPtr<UnownedBaseShape> unowned_;
607 /* For owned BaseShapes, the shape's shape table. */
608 ShapeTable *table_;
610 BaseShape(const BaseShape &base) MOZ_DELETE;
612 public:
613 void finalize(FreeOp *fop);
615 BaseShape(JSCompartment *comp, const Class *clasp, JSObject *parent, JSObject *metadata,
616 uint32_t objectFlags)
617 {
618 JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
619 mozilla::PodZero(this);
620 this->clasp_ = clasp;
621 this->parent = parent;
622 this->metadata = metadata;
623 this->flags = objectFlags;
624 this->compartment_ = comp;
625 }
627 BaseShape(JSCompartment *comp, const Class *clasp, JSObject *parent, JSObject *metadata,
628 uint32_t objectFlags, uint8_t attrs,
629 PropertyOp rawGetter, StrictPropertyOp rawSetter)
630 {
631 JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
632 mozilla::PodZero(this);
633 this->clasp_ = clasp;
634 this->parent = parent;
635 this->metadata = metadata;
636 this->flags = objectFlags;
637 this->rawGetter = rawGetter;
638 this->rawSetter = rawSetter;
639 if ((attrs & JSPROP_GETTER) && rawGetter) {
640 this->flags |= HAS_GETTER_OBJECT;
641 GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->getterObj);
642 }
643 if ((attrs & JSPROP_SETTER) && rawSetter) {
644 this->flags |= HAS_SETTER_OBJECT;
645 GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->setterObj);
646 }
647 this->compartment_ = comp;
648 }
650 inline BaseShape(const StackBaseShape &base);
652 /* Not defined: BaseShapes must not be stack allocated. */
653 ~BaseShape();
655 BaseShape &operator=(const BaseShape &other) {
656 clasp_ = other.clasp_;
657 parent = other.parent;
658 metadata = other.metadata;
659 flags = other.flags;
660 slotSpan_ = other.slotSpan_;
661 if (flags & HAS_GETTER_OBJECT) {
662 getterObj = other.getterObj;
663 GetterSetterWriteBarrierPost(runtimeFromMainThread(), &getterObj);
664 } else {
665 if (rawGetter)
666 GetterSetterWriteBarrierPostRemove(runtimeFromMainThread(), &getterObj);
667 rawGetter = other.rawGetter;
668 }
669 if (flags & HAS_SETTER_OBJECT) {
670 setterObj = other.setterObj;
671 GetterSetterWriteBarrierPost(runtimeFromMainThread(), &setterObj);
672 } else {
673 if (rawSetter)
674 GetterSetterWriteBarrierPostRemove(runtimeFromMainThread(), &setterObj);
675 rawSetter = other.rawSetter;
676 }
677 compartment_ = other.compartment_;
678 return *this;
679 }
681 const Class *clasp() const { return clasp_; }
683 bool isOwned() const { return !!(flags & OWNED_SHAPE); }
685 bool matchesGetterSetter(PropertyOp rawGetter, StrictPropertyOp rawSetter) const {
686 return rawGetter == this->rawGetter && rawSetter == this->rawSetter;
687 }
689 inline void adoptUnowned(UnownedBaseShape *other);
691 void setOwned(UnownedBaseShape *unowned) {
692 flags |= OWNED_SHAPE;
693 this->unowned_ = unowned;
694 }
696 JSObject *getObjectParent() const { return parent; }
697 JSObject *getObjectMetadata() const { return metadata; }
698 uint32_t getObjectFlags() const { return flags & OBJECT_FLAG_MASK; }
700 bool hasGetterObject() const { return !!(flags & HAS_GETTER_OBJECT); }
701 JSObject *getterObject() const { JS_ASSERT(hasGetterObject()); return getterObj; }
703 bool hasSetterObject() const { return !!(flags & HAS_SETTER_OBJECT); }
704 JSObject *setterObject() const { JS_ASSERT(hasSetterObject()); return setterObj; }
706 bool hasTable() const { JS_ASSERT_IF(table_, isOwned()); return table_ != nullptr; }
707 ShapeTable &table() const { JS_ASSERT(table_ && isOwned()); return *table_; }
708 void setTable(ShapeTable *table) { JS_ASSERT(isOwned()); table_ = table; }
710 uint32_t slotSpan() const { JS_ASSERT(isOwned()); return slotSpan_; }
711 void setSlotSpan(uint32_t slotSpan) { JS_ASSERT(isOwned()); slotSpan_ = slotSpan; }
713 JSCompartment *compartment() const { return compartment_; }
715 /*
716 * Lookup base shapes from the compartment's baseShapes table, adding if
717 * not already found.
718 */
719 static UnownedBaseShape* getUnowned(ExclusiveContext *cx, StackBaseShape &base);
721 /*
722 * Lookup base shapes from the compartment's baseShapes table, returning
723 * nullptr if not found.
724 */
725 static UnownedBaseShape *lookupUnowned(ThreadSafeContext *cx, const StackBaseShape &base);
727 /* Get the canonical base shape. */
728 inline UnownedBaseShape* unowned();
730 /* Get the canonical base shape for an owned one. */
731 inline UnownedBaseShape* baseUnowned();
733 /* Get the canonical base shape for an unowned one (i.e. identity). */
734 inline UnownedBaseShape* toUnowned();
736 /* Check that an owned base shape is consistent with its unowned base. */
737 void assertConsistency();
739 /* For JIT usage */
740 static inline size_t offsetOfParent() { return offsetof(BaseShape, parent); }
741 static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); }
743 static inline ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; }
745 void markChildren(JSTracer *trc) {
746 if (hasGetterObject())
747 gc::MarkObjectUnbarriered(trc, &getterObj, "getter");
749 if (hasSetterObject())
750 gc::MarkObjectUnbarriered(trc, &setterObj, "setter");
752 if (isOwned())
753 gc::MarkBaseShape(trc, &unowned_, "base");
755 if (parent)
756 gc::MarkObject(trc, &parent, "parent");
758 if (metadata)
759 gc::MarkObject(trc, &metadata, "metadata");
760 }
762 private:
763 static void staticAsserts() {
764 JS_STATIC_ASSERT(offsetof(BaseShape, clasp_) == offsetof(js::shadow::BaseShape, clasp_));
765 }
766 };
768 class UnownedBaseShape : public BaseShape {};
770 inline void
771 BaseShape::adoptUnowned(UnownedBaseShape *other)
772 {
773 // This is a base shape owned by a dictionary object, update it to reflect the
774 // unowned base shape of a new last property.
775 JS_ASSERT(isOwned());
777 uint32_t span = slotSpan();
778 ShapeTable *table = &this->table();
780 *this = *other;
781 setOwned(other);
782 setTable(table);
783 setSlotSpan(span);
785 assertConsistency();
786 }
788 UnownedBaseShape *
789 BaseShape::unowned()
790 {
791 return isOwned() ? baseUnowned() : toUnowned();
792 }
794 UnownedBaseShape *
795 BaseShape::toUnowned()
796 {
797 JS_ASSERT(!isOwned() && !unowned_); return static_cast<UnownedBaseShape *>(this);
798 }
800 UnownedBaseShape*
801 BaseShape::baseUnowned()
802 {
803 JS_ASSERT(isOwned() && unowned_); return unowned_;
804 }
806 /* Entries for the per-compartment baseShapes set of unowned base shapes. */
807 struct StackBaseShape
808 {
809 typedef const StackBaseShape *Lookup;
811 uint32_t flags;
812 const Class *clasp;
813 JSObject *parent;
814 JSObject *metadata;
815 PropertyOp rawGetter;
816 StrictPropertyOp rawSetter;
817 JSCompartment *compartment;
819 explicit StackBaseShape(BaseShape *base)
820 : flags(base->flags & BaseShape::OBJECT_FLAG_MASK),
821 clasp(base->clasp_),
822 parent(base->parent),
823 metadata(base->metadata),
824 rawGetter(nullptr),
825 rawSetter(nullptr),
826 compartment(base->compartment())
827 {}
829 inline StackBaseShape(ThreadSafeContext *cx, const Class *clasp,
830 JSObject *parent, JSObject *metadata, uint32_t objectFlags);
831 inline StackBaseShape(Shape *shape);
833 void updateGetterSetter(uint8_t attrs, PropertyOp rawGetter, StrictPropertyOp rawSetter) {
834 flags &= ~(BaseShape::HAS_GETTER_OBJECT | BaseShape::HAS_SETTER_OBJECT);
835 if ((attrs & JSPROP_GETTER) && rawGetter) {
836 JS_ASSERT(!IsPoisonedPtr(rawGetter));
837 flags |= BaseShape::HAS_GETTER_OBJECT;
838 }
839 if ((attrs & JSPROP_SETTER) && rawSetter) {
840 JS_ASSERT(!IsPoisonedPtr(rawSetter));
841 flags |= BaseShape::HAS_SETTER_OBJECT;
842 }
844 this->rawGetter = rawGetter;
845 this->rawSetter = rawSetter;
846 }
848 static inline HashNumber hash(const StackBaseShape *lookup);
849 static inline bool match(UnownedBaseShape *key, const StackBaseShape *lookup);
851 // For RootedGeneric<StackBaseShape*>
852 static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; }
853 void trace(JSTracer *trc);
854 };
856 inline
857 BaseShape::BaseShape(const StackBaseShape &base)
858 {
859 mozilla::PodZero(this);
860 this->clasp_ = base.clasp;
861 this->parent = base.parent;
862 this->metadata = base.metadata;
863 this->flags = base.flags;
864 this->rawGetter = base.rawGetter;
865 this->rawSetter = base.rawSetter;
866 if ((base.flags & HAS_GETTER_OBJECT) && base.rawGetter)
867 GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->getterObj);
868 if ((base.flags & HAS_SETTER_OBJECT) && base.rawSetter)
869 GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->setterObj);
870 this->compartment_ = base.compartment;
871 }
873 typedef HashSet<ReadBarriered<UnownedBaseShape>,
874 StackBaseShape,
875 SystemAllocPolicy> BaseShapeSet;
878 class Shape : public gc::BarrieredCell<Shape>
879 {
880 friend class ::JSObject;
881 friend class ::JSFunction;
882 friend class js::Bindings;
883 friend class js::Nursery;
884 friend class js::ObjectImpl;
885 friend class js::PropertyTree;
886 friend class js::StaticBlockObject;
887 friend struct js::StackShape;
888 friend struct js::StackBaseShape;
890 protected:
891 HeapPtrBaseShape base_;
892 EncapsulatedId propid_;
894 JS_ENUM_HEADER(SlotInfo, uint32_t)
895 {
896 /* Number of fixed slots in objects with this shape. */
897 // FIXED_SLOTS_MAX is the biggest count of fixed slots a Shape can store
898 FIXED_SLOTS_MAX = 0x1f,
899 FIXED_SLOTS_SHIFT = 27,
900 FIXED_SLOTS_MASK = uint32_t(FIXED_SLOTS_MAX << FIXED_SLOTS_SHIFT),
902 /*
903 * numLinearSearches starts at zero and is incremented initially on
904 * search() calls. Once numLinearSearches reaches LINEAR_SEARCHES_MAX,
905 * the table is created on the next search() call. The table can also
906 * be created when hashifying for dictionary mode.
907 */
908 LINEAR_SEARCHES_MAX = 0x7,
909 LINEAR_SEARCHES_SHIFT = 24,
910 LINEAR_SEARCHES_MASK = LINEAR_SEARCHES_MAX << LINEAR_SEARCHES_SHIFT,
912 /*
913 * Mask to get the index in object slots for shapes which hasSlot().
914 * For !hasSlot() shapes in the property tree with a parent, stores the
915 * parent's slot index (which may be invalid), and invalid for all
916 * other shapes.
917 */
918 SLOT_MASK = JS_BIT(24) - 1
919 } JS_ENUM_FOOTER(SlotInfo);
921 uint32_t slotInfo; /* mask of above info */
922 uint8_t attrs; /* attributes, see jsapi.h JSPROP_* */
923 uint8_t flags; /* flags, see below for defines */
925 HeapPtrShape parent; /* parent node, reverse for..in order */
926 /* kids is valid when !inDictionary(), listp is valid when inDictionary(). */
927 union {
928 KidsPointer kids; /* null, single child, or a tagged ptr
929 to many-kids data structure */
930 HeapPtrShape *listp; /* dictionary list starting at shape_
931 has a double-indirect back pointer,
932 either to the next shape's parent if not
933 last, else to obj->shape_ */
934 };
936 static inline Shape *search(ExclusiveContext *cx, Shape *start, jsid id,
937 Shape ***pspp, bool adding = false);
938 static inline Shape *searchThreadLocal(ThreadSafeContext *cx, Shape *start, jsid id,
939 Shape ***pspp, bool adding = false);
940 static inline Shape *searchNoHashify(Shape *start, jsid id);
942 void removeFromDictionary(ObjectImpl *obj);
943 void insertIntoDictionary(HeapPtrShape *dictp);
945 void initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp) {
946 new (this) Shape(child, nfixed);
947 this->flags |= IN_DICTIONARY;
949 this->listp = nullptr;
950 insertIntoDictionary(dictp);
951 }
953 /* Replace the base shape of the last shape in a non-dictionary lineage with base. */
954 static Shape *replaceLastProperty(ExclusiveContext *cx, StackBaseShape &base,
955 TaggedProto proto, HandleShape shape);
957 /*
958 * This function is thread safe if every shape in the lineage of |shape|
959 * is thread local, which is the case when we clone the entire shape
960 * lineage in preparation for converting an object to dictionary mode.
961 */
962 static bool hashify(ThreadSafeContext *cx, Shape *shape);
963 void handoffTableTo(Shape *newShape);
965 void setParent(Shape *p) {
966 JS_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(),
967 p->maybeSlot() <= maybeSlot());
968 JS_ASSERT_IF(p && !inDictionary(),
969 hasSlot() == (p->maybeSlot() != maybeSlot()));
970 parent = p;
971 }
973 bool ensureOwnBaseShape(ThreadSafeContext *cx) {
974 if (base()->isOwned())
975 return true;
976 return makeOwnBaseShape(cx);
977 }
979 bool makeOwnBaseShape(ThreadSafeContext *cx);
981 public:
982 bool hasTable() const { return base()->hasTable(); }
983 ShapeTable &table() const { return base()->table(); }
985 void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
986 size_t *propTableSize, size_t *kidsSize) const {
987 if (hasTable())
988 *propTableSize += table().sizeOfIncludingThis(mallocSizeOf);
990 if (!inDictionary() && kids.isHash())
991 *kidsSize += kids.toHash()->sizeOfIncludingThis(mallocSizeOf);
992 }
994 bool isNative() const {
995 JS_ASSERT(!(flags & NON_NATIVE) == getObjectClass()->isNative());
996 return !(flags & NON_NATIVE);
997 }
999 const HeapPtrShape &previous() const { return parent; }
1000 JSCompartment *compartment() const { return base()->compartment(); }
1002 template <AllowGC allowGC>
1003 class Range {
1004 protected:
1005 friend class Shape;
1007 typename MaybeRooted<Shape*, allowGC>::RootType cursor;
1009 public:
1010 Range(ExclusiveContext *cx, Shape *shape) : cursor(cx, shape) {
1011 JS_STATIC_ASSERT(allowGC == CanGC);
1012 }
1014 Range(Shape *shape) : cursor((ExclusiveContext *) nullptr, shape) {
1015 JS_STATIC_ASSERT(allowGC == NoGC);
1016 }
1018 bool empty() const {
1019 return !cursor || cursor->isEmptyShape();
1020 }
1022 Shape &front() const {
1023 JS_ASSERT(!empty());
1024 return *cursor;
1025 }
1027 void popFront() {
1028 JS_ASSERT(!empty());
1029 cursor = cursor->parent;
1030 }
1031 };
1033 const Class *getObjectClass() const {
1034 return base()->clasp_;
1035 }
1036 JSObject *getObjectParent() const { return base()->parent; }
1037 JSObject *getObjectMetadata() const { return base()->metadata; }
1039 static Shape *setObjectParent(ExclusiveContext *cx,
1040 JSObject *obj, TaggedProto proto, Shape *last);
1041 static Shape *setObjectMetadata(JSContext *cx,
1042 JSObject *metadata, TaggedProto proto, Shape *last);
1043 static Shape *setObjectFlag(ExclusiveContext *cx,
1044 BaseShape::Flag flag, TaggedProto proto, Shape *last);
1046 uint32_t getObjectFlags() const { return base()->getObjectFlags(); }
1047 bool hasObjectFlag(BaseShape::Flag flag) const {
1048 JS_ASSERT(!(flag & ~BaseShape::OBJECT_FLAG_MASK));
1049 return !!(base()->flags & flag);
1050 }
1052 protected:
1053 /*
1054 * Implementation-private bits stored in shape->flags. See public: enum {}
1055 * flags further below, which were allocated FCFS over time, so interleave
1056 * with these bits.
1057 */
1058 enum {
1059 /* Property is placeholder for a non-native class. */
1060 NON_NATIVE = 0x01,
1062 /* Property stored in per-object dictionary, not shared property tree. */
1063 IN_DICTIONARY = 0x02,
1065 UNUSED_BITS = 0x3C
1066 };
1068 /* Get a shape identical to this one, without parent/kids information. */
1069 inline Shape(const StackShape &other, uint32_t nfixed);
1071 /* Used by EmptyShape (see jsscopeinlines.h). */
1072 inline Shape(UnownedBaseShape *base, uint32_t nfixed);
1074 /* Copy constructor disabled, to avoid misuse of the above form. */
1075 Shape(const Shape &other) MOZ_DELETE;
1077 /*
1078 * Whether this shape has a valid slot value. This may be true even if
1079 * !hasSlot() (see SlotInfo comment above), and may be false even if
1080 * hasSlot() if the shape is being constructed and has not had a slot
1081 * assigned yet. After construction, hasSlot() implies !hasMissingSlot().
1082 */
1083 bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
1085 public:
1086 bool inDictionary() const {
1087 return (flags & IN_DICTIONARY) != 0;
1088 }
1090 PropertyOp getter() const { return base()->rawGetter; }
1091 bool hasDefaultGetter() const {return !base()->rawGetter; }
1092 PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return base()->rawGetter; }
1093 JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return base()->getterObj; }
1095 // Per ES5, decode null getterObj as the undefined value, which encodes as null.
1096 Value getterValue() const {
1097 JS_ASSERT(hasGetterValue());
1098 return base()->getterObj ? ObjectValue(*base()->getterObj) : UndefinedValue();
1099 }
1101 Value getterOrUndefined() const {
1102 return (hasGetterValue() && base()->getterObj)
1103 ? ObjectValue(*base()->getterObj)
1104 : UndefinedValue();
1105 }
1107 StrictPropertyOp setter() const { return base()->rawSetter; }
1108 bool hasDefaultSetter() const { return !base()->rawSetter; }
1109 StrictPropertyOp setterOp() const { JS_ASSERT(!hasSetterValue()); return base()->rawSetter; }
1110 JSObject *setterObject() const { JS_ASSERT(hasSetterValue()); return base()->setterObj; }
1112 // Per ES5, decode null setterObj as the undefined value, which encodes as null.
1113 Value setterValue() const {
1114 JS_ASSERT(hasSetterValue());
1115 return base()->setterObj ? ObjectValue(*base()->setterObj) : UndefinedValue();
1116 }
1118 Value setterOrUndefined() const {
1119 return (hasSetterValue() && base()->setterObj)
1120 ? ObjectValue(*base()->setterObj)
1121 : UndefinedValue();
1122 }
1124 void update(PropertyOp getter, StrictPropertyOp setter, uint8_t attrs);
1126 bool matches(const Shape *other) const {
1127 return propid_.get() == other->propid_.get() &&
1128 matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs, other->flags);
1129 }
1131 inline bool matches(const StackShape &other) const;
1133 bool matchesParamsAfterId(BaseShape *base, uint32_t aslot, unsigned aattrs, unsigned aflags) const
1134 {
1135 return base->unowned() == this->base()->unowned() &&
1136 maybeSlot() == aslot &&
1137 attrs == aattrs;
1138 }
1140 bool get(JSContext* cx, HandleObject receiver, JSObject *obj, JSObject *pobj, MutableHandleValue vp);
1141 bool set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, MutableHandleValue vp);
1143 BaseShape *base() const { return base_.get(); }
1145 bool hasSlot() const {
1146 return (attrs & JSPROP_SHARED) == 0;
1147 }
1148 uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); }
1149 uint32_t maybeSlot() const {
1150 return slotInfo & SLOT_MASK;
1151 }
1153 bool isEmptyShape() const {
1154 JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
1155 return JSID_IS_EMPTY(propid_);
1156 }
1158 uint32_t slotSpan(const Class *clasp) const {
1159 JS_ASSERT(!inDictionary());
1160 uint32_t free = JSSLOT_FREE(clasp);
1161 return hasMissingSlot() ? free : Max(free, maybeSlot() + 1);
1162 }
1164 uint32_t slotSpan() const {
1165 return slotSpan(getObjectClass());
1166 }
1168 void setSlot(uint32_t slot) {
1169 JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
1170 slotInfo = slotInfo & ~Shape::SLOT_MASK;
1171 slotInfo = slotInfo | slot;
1172 }
1174 uint32_t numFixedSlots() const {
1175 return slotInfo >> FIXED_SLOTS_SHIFT;
1176 }
1178 void setNumFixedSlots(uint32_t nfixed) {
1179 JS_ASSERT(nfixed < FIXED_SLOTS_MAX);
1180 slotInfo = slotInfo & ~FIXED_SLOTS_MASK;
1181 slotInfo = slotInfo | (nfixed << FIXED_SLOTS_SHIFT);
1182 }
1184 uint32_t numLinearSearches() const {
1185 return (slotInfo & LINEAR_SEARCHES_MASK) >> LINEAR_SEARCHES_SHIFT;
1186 }
1188 void incrementNumLinearSearches() {
1189 uint32_t count = numLinearSearches();
1190 JS_ASSERT(count < LINEAR_SEARCHES_MAX);
1191 slotInfo = slotInfo & ~LINEAR_SEARCHES_MASK;
1192 slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT);
1193 }
1195 const EncapsulatedId &propid() const {
1196 JS_ASSERT(!isEmptyShape());
1197 JS_ASSERT(!JSID_IS_VOID(propid_));
1198 return propid_;
1199 }
1200 EncapsulatedId &propidRef() { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
1201 jsid propidRaw() const {
1202 // Return the actual jsid, not an internal reference.
1203 return propid();
1204 }
1206 uint8_t attributes() const { return attrs; }
1207 bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; }
1208 bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; }
1209 bool writable() const {
1210 return (attrs & JSPROP_READONLY) == 0;
1211 }
1212 bool hasGetterValue() const { return attrs & JSPROP_GETTER; }
1213 bool hasSetterValue() const { return attrs & JSPROP_SETTER; }
1215 bool isDataDescriptor() const {
1216 return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
1217 }
1218 bool isAccessorDescriptor() const {
1219 return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
1220 }
1222 PropDesc::Writability writability() const {
1223 return (attrs & JSPROP_READONLY) ? PropDesc::NonWritable : PropDesc::Writable;
1224 }
1225 PropDesc::Enumerability enumerability() const {
1226 return (attrs & JSPROP_ENUMERATE) ? PropDesc::Enumerable : PropDesc::NonEnumerable;
1227 }
1228 PropDesc::Configurability configurability() const {
1229 return (attrs & JSPROP_PERMANENT) ? PropDesc::NonConfigurable : PropDesc::Configurable;
1230 }
1232 /*
1233 * For ES5 compatibility, we allow properties with PropertyOp-flavored
1234 * setters to be shadowed when set. The "own" property thereby created in
1235 * the directly referenced object will have the same getter and setter as
1236 * the prototype property. See bug 552432.
1237 */
1238 bool shadowable() const {
1239 JS_ASSERT_IF(isDataDescriptor(), writable());
1240 return hasSlot() || (attrs & JSPROP_SHADOWABLE);
1241 }
1243 uint32_t entryCount() {
1244 if (hasTable())
1245 return table().entryCount;
1246 uint32_t count = 0;
1247 for (Shape::Range<NoGC> r(this); !r.empty(); r.popFront())
1248 ++count;
1249 return count;
1250 }
1252 bool isBigEnoughForAShapeTable() {
1253 JS_ASSERT(!hasTable());
1254 Shape *shape = this;
1255 uint32_t count = 0;
1256 for (Shape::Range<NoGC> r(shape); !r.empty(); r.popFront()) {
1257 ++count;
1258 if (count >= ShapeTable::MIN_ENTRIES)
1259 return true;
1260 }
1261 return false;
1262 }
1264 #ifdef DEBUG
1265 void dump(JSContext *cx, FILE *fp) const;
1266 void dumpSubtree(JSContext *cx, int level, FILE *fp) const;
1267 #endif
1269 void sweep();
1270 void finalize(FreeOp *fop);
1271 void removeChild(Shape *child);
1273 static inline ThingRootKind rootKind() { return THING_ROOT_SHAPE; }
1275 void markChildren(JSTracer *trc) {
1276 MarkBaseShape(trc, &base_, "base");
1277 gc::MarkId(trc, &propidRef(), "propid");
1278 if (parent)
1279 MarkShape(trc, &parent, "parent");
1280 }
1282 inline Shape *search(ExclusiveContext *cx, jsid id);
1283 inline Shape *searchLinear(jsid id);
1285 /* For JIT usage */
1286 static inline size_t offsetOfBase() { return offsetof(Shape, base_); }
1288 private:
1289 static void staticAsserts() {
1290 JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base));
1291 JS_STATIC_ASSERT(offsetof(Shape, slotInfo) == offsetof(js::shadow::Shape, slotInfo));
1292 JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Shape::FIXED_SLOTS_SHIFT);
1293 static_assert(js::shadow::Object::MAX_FIXED_SLOTS <= FIXED_SLOTS_MAX,
1294 "verify numFixedSlots() bitfield is big enough");
1295 }
1296 };
1298 inline
1299 StackBaseShape::StackBaseShape(Shape *shape)
1300 : flags(shape->getObjectFlags()),
1301 clasp(shape->getObjectClass()),
1302 parent(shape->getObjectParent()),
1303 metadata(shape->getObjectMetadata()),
1304 compartment(shape->compartment())
1305 {
1306 updateGetterSetter(shape->attrs, shape->getter(), shape->setter());
1307 }
1309 class AutoRooterGetterSetter
1310 {
1311 class Inner : private JS::CustomAutoRooter
1312 {
1313 public:
1314 inline Inner(ThreadSafeContext *cx, uint8_t attrs,
1315 PropertyOp *pgetter_, StrictPropertyOp *psetter_);
1317 private:
1318 virtual void trace(JSTracer *trc);
1320 uint8_t attrs;
1321 PropertyOp *pgetter;
1322 StrictPropertyOp *psetter;
1323 };
1325 public:
1326 inline AutoRooterGetterSetter(ThreadSafeContext *cx, uint8_t attrs,
1327 PropertyOp *pgetter, StrictPropertyOp *psetter
1328 MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
1330 private:
1331 mozilla::Maybe<Inner> inner;
1332 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
1333 };
1335 struct EmptyShape : public js::Shape
1336 {
1337 EmptyShape(UnownedBaseShape *base, uint32_t nfixed)
1338 : js::Shape(base, nfixed)
1339 {
1340 // Only empty shapes can be NON_NATIVE.
1341 if (!getObjectClass()->isNative())
1342 flags |= NON_NATIVE;
1343 }
1345 /*
1346 * Lookup an initial shape matching the given parameters, creating an empty
1347 * shape if none was found.
1348 */
1349 static Shape *getInitialShape(ExclusiveContext *cx, const Class *clasp,
1350 TaggedProto proto, JSObject *metadata,
1351 JSObject *parent, size_t nfixed, uint32_t objectFlags = 0);
1352 static Shape *getInitialShape(ExclusiveContext *cx, const Class *clasp,
1353 TaggedProto proto, JSObject *metadata,
1354 JSObject *parent, gc::AllocKind kind, uint32_t objectFlags = 0);
1356 /*
1357 * Reinsert an alternate initial shape, to be returned by future
1358 * getInitialShape calls, until the new shape becomes unreachable in a GC
1359 * and the table entry is purged.
1360 */
1361 static void insertInitialShape(ExclusiveContext *cx, HandleShape shape, HandleObject proto);
1363 /*
1364 * Some object subclasses are allocated with a built-in set of properties.
1365 * The first time such an object is created, these built-in properties must
1366 * be set manually, to compute an initial shape. Afterward, that initial
1367 * shape can be reused for newly-created objects that use the subclass's
1368 * standard prototype. This method should be used in a post-allocation
1369 * init method, to ensure that objects of such subclasses compute and cache
1370 * the initial shape, if it hasn't already been computed.
1371 */
1372 template<class ObjectSubclass>
1373 static inline bool
1374 ensureInitialCustomShape(ExclusiveContext *cx, Handle<ObjectSubclass*> obj);
1375 };
1377 /*
1378 * Entries for the per-compartment initialShapes set indexing initial shapes
1379 * for objects in the compartment and the associated types.
1380 */
1381 struct InitialShapeEntry
1382 {
1383 /*
1384 * Initial shape to give to the object. This is an empty shape, except for
1385 * certain classes (e.g. String, RegExp) which may add certain baked-in
1386 * properties.
1387 */
1388 ReadBarriered<Shape> shape;
1390 /*
1391 * Matching prototype for the entry. The shape of an object determines its
1392 * prototype, but the prototype cannot be determined from the shape itself.
1393 */
1394 TaggedProto proto;
1396 /* State used to determine a match on an initial shape. */
1397 struct Lookup {
1398 const Class *clasp;
1399 TaggedProto hashProto;
1400 TaggedProto matchProto;
1401 JSObject *hashParent;
1402 JSObject *matchParent;
1403 JSObject *hashMetadata;
1404 JSObject *matchMetadata;
1405 uint32_t nfixed;
1406 uint32_t baseFlags;
1407 Lookup(const Class *clasp, TaggedProto proto, JSObject *parent, JSObject *metadata,
1408 uint32_t nfixed, uint32_t baseFlags)
1409 : clasp(clasp),
1410 hashProto(proto), matchProto(proto),
1411 hashParent(parent), matchParent(parent),
1412 hashMetadata(metadata), matchMetadata(metadata),
1413 nfixed(nfixed), baseFlags(baseFlags)
1414 {}
1416 #ifdef JSGC_GENERATIONAL
1417 /*
1418 * For use by generational GC post barriers. Look up an entry whose
1419 * parent and metadata fields may have been moved, but was hashed with
1420 * the original values.
1421 */
1422 Lookup(const Class *clasp, TaggedProto proto,
1423 JSObject *hashParent, JSObject *matchParent,
1424 JSObject *hashMetadata, JSObject *matchMetadata,
1425 uint32_t nfixed, uint32_t baseFlags)
1426 : clasp(clasp),
1427 hashProto(proto), matchProto(proto),
1428 hashParent(hashParent), matchParent(matchParent),
1429 hashMetadata(hashMetadata), matchMetadata(matchMetadata),
1430 nfixed(nfixed), baseFlags(baseFlags)
1431 {}
1432 #endif
1433 };
1435 inline InitialShapeEntry();
1436 inline InitialShapeEntry(const ReadBarriered<Shape> &shape, TaggedProto proto);
1438 inline Lookup getLookup() const;
1440 static inline HashNumber hash(const Lookup &lookup);
1441 static inline bool match(const InitialShapeEntry &key, const Lookup &lookup);
1442 static void rekey(InitialShapeEntry &k, const InitialShapeEntry& newKey) { k = newKey; }
1443 };
1445 typedef HashSet<InitialShapeEntry, InitialShapeEntry, SystemAllocPolicy> InitialShapeSet;
1447 struct StackShape
1448 {
1449 /* For performance, StackShape only roots when absolutely necessary. */
1450 UnownedBaseShape *base;
1451 jsid propid;
1452 uint32_t slot_;
1453 uint8_t attrs;
1454 uint8_t flags;
1456 explicit StackShape(UnownedBaseShape *base, jsid propid, uint32_t slot,
1457 unsigned attrs, unsigned flags)
1458 : base(base),
1459 propid(propid),
1460 slot_(slot),
1461 attrs(uint8_t(attrs)),
1462 flags(uint8_t(flags))
1463 {
1464 JS_ASSERT(base);
1465 JS_ASSERT(!JSID_IS_VOID(propid));
1466 JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
1467 JS_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
1468 }
1470 StackShape(Shape *shape)
1471 : base(shape->base()->unowned()),
1472 propid(shape->propidRef()),
1473 slot_(shape->maybeSlot()),
1474 attrs(shape->attrs),
1475 flags(shape->flags)
1476 {}
1478 bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
1479 bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
1481 uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; }
1482 uint32_t maybeSlot() const { return slot_; }
1484 uint32_t slotSpan() const {
1485 uint32_t free = JSSLOT_FREE(base->clasp_);
1486 return hasMissingSlot() ? free : (maybeSlot() + 1);
1487 }
1489 void setSlot(uint32_t slot) {
1490 JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
1491 slot_ = slot;
1492 }
1494 HashNumber hash() const {
1495 HashNumber hash = uintptr_t(base);
1497 /* Accumulate from least to most random so the low bits are most random. */
1498 hash = mozilla::RotateLeft(hash, 4) ^ attrs;
1499 hash = mozilla::RotateLeft(hash, 4) ^ slot_;
1500 hash = mozilla::RotateLeft(hash, 4) ^ JSID_BITS(propid);
1501 return hash;
1502 }
1504 // For RootedGeneric<StackShape*>
1505 static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; }
1506 void trace(JSTracer *trc);
1507 };
1509 } /* namespace js */
1511 /* js::Shape pointer tag bit indicating a collision. */
1512 #define SHAPE_COLLISION (uintptr_t(1))
1513 #define SHAPE_REMOVED ((js::Shape *) SHAPE_COLLISION)
1515 /* Functions to get and set shape pointer values and collision flags. */
1517 inline bool
1518 SHAPE_IS_FREE(js::Shape *shape)
1519 {
1520 return shape == nullptr;
1521 }
1523 inline bool
1524 SHAPE_IS_REMOVED(js::Shape *shape)
1525 {
1526 return shape == SHAPE_REMOVED;
1527 }
1529 inline bool
1530 SHAPE_IS_LIVE(js::Shape *shape)
1531 {
1532 return shape > SHAPE_REMOVED;
1533 }
1535 inline void
1536 SHAPE_FLAG_COLLISION(js::Shape **spp, js::Shape *shape)
1537 {
1538 *spp = reinterpret_cast<js::Shape*>(uintptr_t(shape) | SHAPE_COLLISION);
1539 }
1541 inline bool
1542 SHAPE_HAD_COLLISION(js::Shape *shape)
1543 {
1544 return uintptr_t(shape) & SHAPE_COLLISION;
1545 }
1547 inline js::Shape *
1548 SHAPE_CLEAR_COLLISION(js::Shape *shape)
1549 {
1550 return reinterpret_cast<js::Shape*>(uintptr_t(shape) & ~SHAPE_COLLISION);
1551 }
1553 inline js::Shape *
1554 SHAPE_FETCH(js::Shape **spp)
1555 {
1556 return SHAPE_CLEAR_COLLISION(*spp);
1557 }
1559 inline void
1560 SHAPE_STORE_PRESERVING_COLLISION(js::Shape **spp, js::Shape *shape)
1561 {
1562 *spp = reinterpret_cast<js::Shape*>(uintptr_t(shape) |
1563 uintptr_t(SHAPE_HAD_COLLISION(*spp)));
1564 }
1566 namespace js {
1568 inline
1569 Shape::Shape(const StackShape &other, uint32_t nfixed)
1570 : base_(other.base),
1571 propid_(other.propid),
1572 slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)),
1573 attrs(other.attrs),
1574 flags(other.flags),
1575 parent(nullptr)
1576 {
1577 JS_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
1578 kids.setNull();
1579 }
1581 inline
1582 Shape::Shape(UnownedBaseShape *base, uint32_t nfixed)
1583 : base_(base),
1584 propid_(JSID_EMPTY),
1585 slotInfo(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)),
1586 attrs(JSPROP_SHARED),
1587 flags(0),
1588 parent(nullptr)
1589 {
1590 JS_ASSERT(base);
1591 kids.setNull();
1592 }
1594 inline Shape *
1595 Shape::searchLinear(jsid id)
1596 {
1597 /*
1598 * Non-dictionary shapes can acquire a table at any point the main thread
1599 * is operating on it, so other threads inspecting such shapes can't use
1600 * their table without racing. This function can be called from any thread
1601 * on any non-dictionary shape.
1602 */
1603 JS_ASSERT(!inDictionary());
1605 for (Shape *shape = this; shape; ) {
1606 if (shape->propidRef() == id)
1607 return shape;
1608 shape = shape->parent;
1609 }
1611 return nullptr;
1612 }
1614 /*
1615 * Keep this function in sync with search. It neither hashifies the start
1616 * shape nor increments linear search count.
1617 */
1618 inline Shape *
1619 Shape::searchNoHashify(Shape *start, jsid id)
1620 {
1621 /*
1622 * If we have a table, search in the shape table, else do a linear
1623 * search. We never hashify into a table in parallel.
1624 */
1625 if (start->hasTable()) {
1626 Shape **spp = start->table().search(id, false);
1627 return SHAPE_FETCH(spp);
1628 }
1630 return start->searchLinear(id);
1631 }
1633 inline bool
1634 Shape::matches(const StackShape &other) const
1635 {
1636 return propid_.get() == other.propid &&
1637 matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags);
1638 }
1640 template<> struct RootKind<Shape *> : SpecificRootKind<Shape *, THING_ROOT_SHAPE> {};
1641 template<> struct RootKind<BaseShape *> : SpecificRootKind<BaseShape *, THING_ROOT_BASE_SHAPE> {};
1643 // Property lookup hooks on objects are required to return a non-nullptr shape
1644 // to signify that the property has been found. For cases where the property is
1645 // not actually represented by a Shape, use a dummy value. This includes all
1646 // properties of non-native objects, and dense elements for native objects.
1647 // Use separate APIs for these two cases.
1649 static inline void
1650 MarkNonNativePropertyFound(MutableHandleShape propp)
1651 {
1652 propp.set(reinterpret_cast<Shape*>(1));
1653 }
1655 template <AllowGC allowGC>
1656 static inline void
1657 MarkDenseOrTypedArrayElementFound(typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
1658 {
1659 propp.set(reinterpret_cast<Shape*>(1));
1660 }
1662 static inline bool
1663 IsImplicitDenseOrTypedArrayElement(Shape *prop)
1664 {
1665 return prop == reinterpret_cast<Shape*>(1);
1666 }
1668 } // namespace js
1670 #ifdef _MSC_VER
1671 #pragma warning(pop)
1672 #pragma warning(pop)
1673 #endif
1675 namespace JS {
1676 template<> class AnchorPermitted<js::Shape *> { };
1677 template<> class AnchorPermitted<const js::Shape *> { };
1678 }
1680 #endif /* vm_Shape_h */