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