js/src/vm/Shape.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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 */

mercurial