js/src/vm/Shape.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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);
  1014         Range(Shape *shape) : cursor((ExclusiveContext *) nullptr, shape) {
  1015             JS_STATIC_ASSERT(allowGC == NoGC);
  1018         bool empty() const {
  1019             return !cursor || cursor->isEmptyShape();
  1022         Shape &front() const {
  1023             JS_ASSERT(!empty());
  1024             return *cursor;
  1027         void popFront() {
  1028             JS_ASSERT(!empty());
  1029             cursor = cursor->parent;
  1031     };
  1033     const Class *getObjectClass() const {
  1034         return base()->clasp_;
  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);
  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;
  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();
  1101     Value getterOrUndefined() const {
  1102         return (hasGetterValue() && base()->getterObj)
  1103                ? ObjectValue(*base()->getterObj)
  1104                : UndefinedValue();
  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();
  1118     Value setterOrUndefined() const {
  1119         return (hasSetterValue() && base()->setterObj)
  1120                ? ObjectValue(*base()->setterObj)
  1121                : UndefinedValue();
  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);
  1131     inline bool matches(const StackShape &other) const;
  1133     bool matchesParamsAfterId(BaseShape *base, uint32_t aslot, unsigned aattrs, unsigned aflags) const
  1135         return base->unowned() == this->base()->unowned() &&
  1136                maybeSlot() == aslot &&
  1137                attrs == aattrs;
  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;
  1148     uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); }
  1149     uint32_t maybeSlot() const {
  1150         return slotInfo & SLOT_MASK;
  1153     bool isEmptyShape() const {
  1154         JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
  1155         return JSID_IS_EMPTY(propid_);
  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);
  1164     uint32_t slotSpan() const {
  1165         return slotSpan(getObjectClass());
  1168     void setSlot(uint32_t slot) {
  1169         JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
  1170         slotInfo = slotInfo & ~Shape::SLOT_MASK;
  1171         slotInfo = slotInfo | slot;
  1174     uint32_t numFixedSlots() const {
  1175         return slotInfo >> FIXED_SLOTS_SHIFT;
  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);
  1184     uint32_t numLinearSearches() const {
  1185         return (slotInfo & LINEAR_SEARCHES_MASK) >> LINEAR_SEARCHES_SHIFT;
  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);
  1195     const EncapsulatedId &propid() const {
  1196         JS_ASSERT(!isEmptyShape());
  1197         JS_ASSERT(!JSID_IS_VOID(propid_));
  1198         return propid_;
  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();
  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;
  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;
  1218     bool isAccessorDescriptor() const {
  1219         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
  1222     PropDesc::Writability writability() const {
  1223         return (attrs & JSPROP_READONLY) ? PropDesc::NonWritable : PropDesc::Writable;
  1225     PropDesc::Enumerability enumerability() const {
  1226         return (attrs & JSPROP_ENUMERATE) ? PropDesc::Enumerable : PropDesc::NonEnumerable;
  1228     PropDesc::Configurability configurability() const {
  1229         return (attrs & JSPROP_PERMANENT) ? PropDesc::NonConfigurable : PropDesc::Configurable;
  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);
  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;
  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;
  1261         return false;
  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");
  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");
  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())
  1306     updateGetterSetter(shape->attrs, shape->getter(), shape->setter());
  1309 class AutoRooterGetterSetter
  1311     class Inner : private JS::CustomAutoRooter
  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
  1337     EmptyShape(UnownedBaseShape *base, uint32_t nfixed)
  1338       : js::Shape(base, nfixed)
  1340         // Only empty shapes can be NON_NATIVE.
  1341         if (!getObjectClass()->isNative())
  1342             flags |= NON_NATIVE;
  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
  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
  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))
  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);
  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);
  1489     void setSlot(uint32_t slot) {
  1490         JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
  1491         slot_ = slot;
  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;
  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)
  1520     return shape == nullptr;
  1523 inline bool
  1524 SHAPE_IS_REMOVED(js::Shape *shape)
  1526     return shape == SHAPE_REMOVED;
  1529 inline bool
  1530 SHAPE_IS_LIVE(js::Shape *shape)
  1532     return shape > SHAPE_REMOVED;
  1535 inline void
  1536 SHAPE_FLAG_COLLISION(js::Shape **spp, js::Shape *shape)
  1538     *spp = reinterpret_cast<js::Shape*>(uintptr_t(shape) | SHAPE_COLLISION);
  1541 inline bool
  1542 SHAPE_HAD_COLLISION(js::Shape *shape)
  1544     return uintptr_t(shape) & SHAPE_COLLISION;
  1547 inline js::Shape *
  1548 SHAPE_CLEAR_COLLISION(js::Shape *shape)
  1550     return reinterpret_cast<js::Shape*>(uintptr_t(shape) & ~SHAPE_COLLISION);
  1553 inline js::Shape *
  1554 SHAPE_FETCH(js::Shape **spp)
  1556     return SHAPE_CLEAR_COLLISION(*spp);
  1559 inline void
  1560 SHAPE_STORE_PRESERVING_COLLISION(js::Shape **spp, js::Shape *shape)
  1562     *spp = reinterpret_cast<js::Shape*>(uintptr_t(shape) |
  1563                                         uintptr_t(SHAPE_HAD_COLLISION(*spp)));
  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)
  1577     JS_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
  1578     kids.setNull();
  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)
  1590     JS_ASSERT(base);
  1591     kids.setNull();
  1594 inline Shape *
  1595 Shape::searchLinear(jsid id)
  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;
  1611     return nullptr;
  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)
  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);
  1630     return start->searchLinear(id);
  1633 inline bool
  1634 Shape::matches(const StackShape &other) const
  1636     return propid_.get() == other.propid &&
  1637            matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags);
  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)
  1652     propp.set(reinterpret_cast<Shape*>(1));
  1655 template <AllowGC allowGC>
  1656 static inline void
  1657 MarkDenseOrTypedArrayElementFound(typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
  1659     propp.set(reinterpret_cast<Shape*>(1));
  1662 static inline bool
  1663 IsImplicitDenseOrTypedArrayElement(Shape *prop)
  1665     return prop == reinterpret_cast<Shape*>(1);
  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 *> { };
  1680 #endif /* vm_Shape_h */

mercurial