1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/vm/Shape.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1680 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef vm_Shape_h 1.11 +#define vm_Shape_h 1.12 + 1.13 +#include "mozilla/Attributes.h" 1.14 +#include "mozilla/GuardObjects.h" 1.15 +#include "mozilla/MathAlgorithms.h" 1.16 +#include "mozilla/Maybe.h" 1.17 +#include "mozilla/MemoryReporting.h" 1.18 +#include "mozilla/TemplateLib.h" 1.19 + 1.20 +#include "jsapi.h" 1.21 +#include "jsfriendapi.h" 1.22 +#include "jsinfer.h" 1.23 +#include "jspropertytree.h" 1.24 +#include "jstypes.h" 1.25 +#include "NamespaceImports.h" 1.26 + 1.27 +#include "gc/Barrier.h" 1.28 +#include "gc/Heap.h" 1.29 +#include "gc/Marking.h" 1.30 +#include "gc/Rooting.h" 1.31 +#include "js/HashTable.h" 1.32 +#include "js/RootingAPI.h" 1.33 + 1.34 +#ifdef _MSC_VER 1.35 +#pragma warning(push) 1.36 +#pragma warning(disable:4800) 1.37 +#pragma warning(push) 1.38 +#pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */ 1.39 +#endif 1.40 + 1.41 +/* 1.42 + * In isolation, a Shape represents a property that exists in one or more 1.43 + * objects; it has an id, flags, etc. (But it doesn't represent the property's 1.44 + * value.) However, Shapes are always stored in linked linear sequence of 1.45 + * Shapes, called "shape lineages". Each shape lineage represents the layout of 1.46 + * an entire object. 1.47 + * 1.48 + * Every JSObject has a pointer, |shape_|, accessible via lastProperty(), to 1.49 + * the last Shape in a shape lineage, which identifies the property most 1.50 + * recently added to the object. This pointer permits fast object layout 1.51 + * tests. The shape lineage order also dictates the enumeration order for the 1.52 + * object; ECMA requires no particular order but this implementation has 1.53 + * promised and delivered property definition order. 1.54 + * 1.55 + * Shape lineages occur in two kinds of data structure. 1.56 + * 1.57 + * 1. N-ary property trees. Each path from a non-root node to the root node in 1.58 + * a property tree is a shape lineage. Property trees permit full (or 1.59 + * partial) sharing of Shapes between objects that have fully (or partly) 1.60 + * identical layouts. The root is an EmptyShape whose identity is determined 1.61 + * by the object's class, compartment and prototype. These Shapes are shared 1.62 + * and immutable. 1.63 + * 1.64 + * 2. Dictionary mode lists. Shapes in such lists are said to be "in 1.65 + * dictionary mode", as are objects that point to such Shapes. These Shapes 1.66 + * are unshared, private to a single object, and immutable except for their 1.67 + * links in the dictionary list. 1.68 + * 1.69 + * All shape lineages are bi-directionally linked, via the |parent| and 1.70 + * |kids|/|listp| members. 1.71 + * 1.72 + * Shape lineages start out life in the property tree. They can be converted 1.73 + * (by copying) to dictionary mode lists in the following circumstances. 1.74 + * 1.75 + * 1. The shape lineage's size reaches MAX_HEIGHT. This reasonable limit avoids 1.76 + * potential worst cases involving shape lineage mutations. 1.77 + * 1.78 + * 2. A property represented by a non-last Shape in a shape lineage is removed 1.79 + * from an object. (In the last Shape case, obj->shape_ can be easily 1.80 + * adjusted to point to obj->shape_->parent.) We originally tried lazy 1.81 + * forking of the property tree, but this blows up for delete/add 1.82 + * repetitions. 1.83 + * 1.84 + * 3. A property represented by a non-last Shape in a shape lineage has its 1.85 + * attributes modified. 1.86 + * 1.87 + * To find the Shape for a particular property of an object initially requires 1.88 + * a linear search. But if the number of searches starting at any particular 1.89 + * Shape in the property tree exceeds MAX_LINEAR_SEARCHES and the Shape's 1.90 + * lineage has (excluding the EmptyShape) at least MIN_ENTRIES, we create an 1.91 + * auxiliary hash table -- the ShapeTable -- that allows faster lookup. 1.92 + * Furthermore, a ShapeTable is always created for dictionary mode lists, 1.93 + * and it is attached to the last Shape in the lineage. Shape tables for 1.94 + * property tree Shapes never change, but shape tables for dictionary mode 1.95 + * Shapes can grow and shrink. 1.96 + * 1.97 + * There used to be a long, math-heavy comment here explaining why property 1.98 + * trees are more space-efficient than alternatives. This was removed in bug 1.99 + * 631138; see that bug for the full details. 1.100 + * 1.101 + * Because many Shapes have similar data, there is actually a secondary type 1.102 + * called a BaseShape that holds some of a Shape's data. Many shapes can share 1.103 + * a single BaseShape. 1.104 + */ 1.105 + 1.106 +#define JSSLOT_FREE(clasp) JSCLASS_RESERVED_SLOTS(clasp) 1.107 + 1.108 +namespace js { 1.109 + 1.110 +class Bindings; 1.111 +class Debugger; 1.112 +class Nursery; 1.113 +class ObjectImpl; 1.114 +class StaticBlockObject; 1.115 + 1.116 +typedef JSPropertyOp PropertyOp; 1.117 +typedef JSStrictPropertyOp StrictPropertyOp; 1.118 +typedef JSPropertyDescriptor PropertyDescriptor; 1.119 + 1.120 +/* Limit on the number of slotful properties in an object. */ 1.121 +static const uint32_t SHAPE_INVALID_SLOT = JS_BIT(24) - 1; 1.122 +static const uint32_t SHAPE_MAXIMUM_SLOT = JS_BIT(24) - 2; 1.123 + 1.124 +static inline PropertyOp 1.125 +CastAsPropertyOp(JSObject *object) 1.126 +{ 1.127 + return JS_DATA_TO_FUNC_PTR(PropertyOp, object); 1.128 +} 1.129 + 1.130 +static inline StrictPropertyOp 1.131 +CastAsStrictPropertyOp(JSObject *object) 1.132 +{ 1.133 + return JS_DATA_TO_FUNC_PTR(StrictPropertyOp, object); 1.134 +} 1.135 + 1.136 +/* 1.137 + * A representation of ECMA-262 ed. 5's internal Property Descriptor data 1.138 + * structure. 1.139 + */ 1.140 +struct PropDesc { 1.141 + private: 1.142 + /* 1.143 + * Original object from which this descriptor derives, passed through for 1.144 + * the benefit of proxies. FIXME: Remove this when direct proxies happen. 1.145 + */ 1.146 + Value pd_; 1.147 + 1.148 + Value value_, get_, set_; 1.149 + 1.150 + /* Property descriptor boolean fields. */ 1.151 + uint8_t attrs; 1.152 + 1.153 + /* Bits indicating which values are set. */ 1.154 + bool hasGet_ : 1; 1.155 + bool hasSet_ : 1; 1.156 + bool hasValue_ : 1; 1.157 + bool hasWritable_ : 1; 1.158 + bool hasEnumerable_ : 1; 1.159 + bool hasConfigurable_ : 1; 1.160 + 1.161 + /* Or maybe this represents a property's absence, and it's undefined. */ 1.162 + bool isUndefined_ : 1; 1.163 + 1.164 + PropDesc(const Value &v) 1.165 + : pd_(UndefinedValue()), 1.166 + value_(v), 1.167 + get_(UndefinedValue()), set_(UndefinedValue()), 1.168 + attrs(0), 1.169 + hasGet_(false), hasSet_(false), 1.170 + hasValue_(true), hasWritable_(false), hasEnumerable_(false), hasConfigurable_(false), 1.171 + isUndefined_(false) 1.172 + { 1.173 + } 1.174 + 1.175 + public: 1.176 + friend class AutoPropDescRooter; 1.177 + friend void JS::AutoGCRooter::trace(JSTracer *trc); 1.178 + 1.179 + enum Enumerability { Enumerable = true, NonEnumerable = false }; 1.180 + enum Configurability { Configurable = true, NonConfigurable = false }; 1.181 + enum Writability { Writable = true, NonWritable = false }; 1.182 + 1.183 + PropDesc(); 1.184 + 1.185 + static PropDesc undefined() { return PropDesc(); } 1.186 + static PropDesc valueOnly(const Value &v) { return PropDesc(v); } 1.187 + 1.188 + PropDesc(const Value &v, Writability writable, 1.189 + Enumerability enumerable, Configurability configurable) 1.190 + : pd_(UndefinedValue()), 1.191 + value_(v), 1.192 + get_(UndefinedValue()), set_(UndefinedValue()), 1.193 + attrs((writable ? 0 : JSPROP_READONLY) | 1.194 + (enumerable ? JSPROP_ENUMERATE : 0) | 1.195 + (configurable ? 0 : JSPROP_PERMANENT)), 1.196 + hasGet_(false), hasSet_(false), 1.197 + hasValue_(true), hasWritable_(true), hasEnumerable_(true), hasConfigurable_(true), 1.198 + isUndefined_(false) 1.199 + {} 1.200 + 1.201 + inline PropDesc(const Value &getter, const Value &setter, 1.202 + Enumerability enumerable, Configurability configurable); 1.203 + 1.204 + /* 1.205 + * 8.10.5 ToPropertyDescriptor(Obj) 1.206 + * 1.207 + * If checkAccessors is false, skip steps 7.b and 8.b, which throw a 1.208 + * TypeError if .get or .set is neither a callable object nor undefined. 1.209 + * 1.210 + * (DebuggerObject_defineProperty uses this: the .get and .set properties 1.211 + * are expected to be Debugger.Object wrappers of functions, which are not 1.212 + * themselves callable.) 1.213 + */ 1.214 + bool initialize(JSContext *cx, const Value &v, bool checkAccessors = true); 1.215 + 1.216 + /* 1.217 + * If IsGenericDescriptor(desc) or IsDataDescriptor(desc) is true, then if 1.218 + * the value of an attribute field of desc, considered as a data 1.219 + * descriptor, is absent, set it to its default value. Else if the value of 1.220 + * an attribute field of desc, considered as an attribute descriptor, is 1.221 + * absent, set it to its default value. 1.222 + */ 1.223 + void complete(); 1.224 + 1.225 + /* 1.226 + * 8.10.4 FromPropertyDescriptor(Desc) 1.227 + * 1.228 + * initFromPropertyDescriptor sets pd to undefined and populates all the 1.229 + * other fields of this PropDesc from desc. 1.230 + * 1.231 + * makeObject populates pd based on the other fields of *this, creating a 1.232 + * new property descriptor JSObject and defining properties on it. 1.233 + */ 1.234 + void initFromPropertyDescriptor(Handle<PropertyDescriptor> desc); 1.235 + bool makeObject(JSContext *cx); 1.236 + 1.237 + void setUndefined() { isUndefined_ = true; } 1.238 + 1.239 + bool isUndefined() const { return isUndefined_; } 1.240 + 1.241 + bool hasGet() const { MOZ_ASSERT(!isUndefined()); return hasGet_; } 1.242 + bool hasSet() const { MOZ_ASSERT(!isUndefined()); return hasSet_; } 1.243 + bool hasValue() const { MOZ_ASSERT(!isUndefined()); return hasValue_; } 1.244 + bool hasWritable() const { MOZ_ASSERT(!isUndefined()); return hasWritable_; } 1.245 + bool hasEnumerable() const { MOZ_ASSERT(!isUndefined()); return hasEnumerable_; } 1.246 + bool hasConfigurable() const { MOZ_ASSERT(!isUndefined()); return hasConfigurable_; } 1.247 + 1.248 + Value pd() const { MOZ_ASSERT(!isUndefined()); return pd_; } 1.249 + void clearPd() { pd_ = UndefinedValue(); } 1.250 + 1.251 + uint8_t attributes() const { MOZ_ASSERT(!isUndefined()); return attrs; } 1.252 + 1.253 + /* 8.10.1 IsAccessorDescriptor(desc) */ 1.254 + bool isAccessorDescriptor() const { 1.255 + return !isUndefined() && (hasGet() || hasSet()); 1.256 + } 1.257 + 1.258 + /* 8.10.2 IsDataDescriptor(desc) */ 1.259 + bool isDataDescriptor() const { 1.260 + return !isUndefined() && (hasValue() || hasWritable()); 1.261 + } 1.262 + 1.263 + /* 8.10.3 IsGenericDescriptor(desc) */ 1.264 + bool isGenericDescriptor() const { 1.265 + return !isUndefined() && !isAccessorDescriptor() && !isDataDescriptor(); 1.266 + } 1.267 + 1.268 + bool configurable() const { 1.269 + MOZ_ASSERT(!isUndefined()); 1.270 + MOZ_ASSERT(hasConfigurable()); 1.271 + return (attrs & JSPROP_PERMANENT) == 0; 1.272 + } 1.273 + 1.274 + bool enumerable() const { 1.275 + MOZ_ASSERT(!isUndefined()); 1.276 + MOZ_ASSERT(hasEnumerable()); 1.277 + return (attrs & JSPROP_ENUMERATE) != 0; 1.278 + } 1.279 + 1.280 + bool writable() const { 1.281 + MOZ_ASSERT(!isUndefined()); 1.282 + MOZ_ASSERT(hasWritable()); 1.283 + return (attrs & JSPROP_READONLY) == 0; 1.284 + } 1.285 + 1.286 + HandleValue value() const { 1.287 + MOZ_ASSERT(hasValue()); 1.288 + return HandleValue::fromMarkedLocation(&value_); 1.289 + } 1.290 + 1.291 + JSObject * getterObject() const { 1.292 + MOZ_ASSERT(!isUndefined()); 1.293 + MOZ_ASSERT(hasGet()); 1.294 + return get_.isUndefined() ? nullptr : &get_.toObject(); 1.295 + } 1.296 + JSObject * setterObject() const { 1.297 + MOZ_ASSERT(!isUndefined()); 1.298 + MOZ_ASSERT(hasSet()); 1.299 + return set_.isUndefined() ? nullptr : &set_.toObject(); 1.300 + } 1.301 + 1.302 + HandleValue getterValue() const { 1.303 + MOZ_ASSERT(!isUndefined()); 1.304 + MOZ_ASSERT(hasGet()); 1.305 + return HandleValue::fromMarkedLocation(&get_); 1.306 + } 1.307 + HandleValue setterValue() const { 1.308 + MOZ_ASSERT(!isUndefined()); 1.309 + MOZ_ASSERT(hasSet()); 1.310 + return HandleValue::fromMarkedLocation(&set_); 1.311 + } 1.312 + 1.313 + /* 1.314 + * Unfortunately the values produced by these methods are used such that 1.315 + * we can't assert anything here. :-( 1.316 + */ 1.317 + PropertyOp getter() const { 1.318 + return CastAsPropertyOp(get_.isUndefined() ? nullptr : &get_.toObject()); 1.319 + } 1.320 + StrictPropertyOp setter() const { 1.321 + return CastAsStrictPropertyOp(set_.isUndefined() ? nullptr : &set_.toObject()); 1.322 + } 1.323 + 1.324 + /* 1.325 + * Throw a TypeError if a getter/setter is present and is neither callable 1.326 + * nor undefined. These methods do exactly the type checks that are skipped 1.327 + * by passing false as the checkAccessors parameter of initialize. 1.328 + */ 1.329 + bool checkGetter(JSContext *cx); 1.330 + bool checkSetter(JSContext *cx); 1.331 + 1.332 + bool unwrapDebuggerObjectsInto(JSContext *cx, Debugger *dbg, HandleObject obj, 1.333 + PropDesc *unwrapped) const; 1.334 + 1.335 + bool wrapInto(JSContext *cx, HandleObject obj, const jsid &id, jsid *wrappedId, 1.336 + PropDesc *wrappedDesc) const; 1.337 +}; 1.338 + 1.339 +class AutoPropDescRooter : private JS::CustomAutoRooter 1.340 +{ 1.341 + public: 1.342 + explicit AutoPropDescRooter(JSContext *cx 1.343 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 1.344 + : CustomAutoRooter(cx) 1.345 + { 1.346 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.347 + } 1.348 + 1.349 + PropDesc& getPropDesc() { return propDesc; } 1.350 + 1.351 + void initFromPropertyDescriptor(Handle<PropertyDescriptor> desc) { 1.352 + propDesc.initFromPropertyDescriptor(desc); 1.353 + } 1.354 + 1.355 + bool makeObject(JSContext *cx) { 1.356 + return propDesc.makeObject(cx); 1.357 + } 1.358 + 1.359 + void setUndefined() { propDesc.setUndefined(); } 1.360 + bool isUndefined() const { return propDesc.isUndefined(); } 1.361 + 1.362 + bool hasGet() const { return propDesc.hasGet(); } 1.363 + bool hasSet() const { return propDesc.hasSet(); } 1.364 + bool hasValue() const { return propDesc.hasValue(); } 1.365 + bool hasWritable() const { return propDesc.hasWritable(); } 1.366 + bool hasEnumerable() const { return propDesc.hasEnumerable(); } 1.367 + bool hasConfigurable() const { return propDesc.hasConfigurable(); } 1.368 + 1.369 + Value pd() const { return propDesc.pd(); } 1.370 + void clearPd() { propDesc.clearPd(); } 1.371 + 1.372 + uint8_t attributes() const { return propDesc.attributes(); } 1.373 + 1.374 + bool isAccessorDescriptor() const { return propDesc.isAccessorDescriptor(); } 1.375 + bool isDataDescriptor() const { return propDesc.isDataDescriptor(); } 1.376 + bool isGenericDescriptor() const { return propDesc.isGenericDescriptor(); } 1.377 + bool configurable() const { return propDesc.configurable(); } 1.378 + bool enumerable() const { return propDesc.enumerable(); } 1.379 + bool writable() const { return propDesc.writable(); } 1.380 + 1.381 + HandleValue value() const { return propDesc.value(); } 1.382 + JSObject *getterObject() const { return propDesc.getterObject(); } 1.383 + JSObject *setterObject() const { return propDesc.setterObject(); } 1.384 + HandleValue getterValue() const { return propDesc.getterValue(); } 1.385 + HandleValue setterValue() const { return propDesc.setterValue(); } 1.386 + 1.387 + PropertyOp getter() const { return propDesc.getter(); } 1.388 + StrictPropertyOp setter() const { return propDesc.setter(); } 1.389 + 1.390 + private: 1.391 + virtual void trace(JSTracer *trc); 1.392 + 1.393 + PropDesc propDesc; 1.394 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.395 +}; 1.396 + 1.397 +/* 1.398 + * Shapes use multiplicative hashing, but specialized to 1.399 + * minimize footprint. 1.400 + */ 1.401 +struct ShapeTable { 1.402 + static const uint32_t HASH_BITS = mozilla::tl::BitSize<HashNumber>::value; 1.403 + static const uint32_t MIN_ENTRIES = 7; 1.404 + static const uint32_t MIN_SIZE_LOG2 = 4; 1.405 + static const uint32_t MIN_SIZE = JS_BIT(MIN_SIZE_LOG2); 1.406 + 1.407 + int hashShift; /* multiplicative hash shift */ 1.408 + 1.409 + uint32_t entryCount; /* number of entries in table */ 1.410 + uint32_t removedCount; /* removed entry sentinels in table */ 1.411 + uint32_t freelist; /* SHAPE_INVALID_SLOT or head of slot 1.412 + freelist in owning dictionary-mode 1.413 + object */ 1.414 + js::Shape **entries; /* table of ptrs to shared tree nodes */ 1.415 + 1.416 + ShapeTable(uint32_t nentries) 1.417 + : hashShift(HASH_BITS - MIN_SIZE_LOG2), 1.418 + entryCount(nentries), 1.419 + removedCount(0), 1.420 + freelist(SHAPE_INVALID_SLOT) 1.421 + { 1.422 + /* NB: entries is set by init, which must be called. */ 1.423 + } 1.424 + 1.425 + ~ShapeTable() { 1.426 + js_free(entries); 1.427 + } 1.428 + 1.429 + /* By definition, hashShift = HASH_BITS - log2(capacity). */ 1.430 + uint32_t capacity() const { return JS_BIT(HASH_BITS - hashShift); } 1.431 + 1.432 + /* Computes the size of the entries array for a given capacity. */ 1.433 + static size_t sizeOfEntries(size_t cap) { return cap * sizeof(Shape *); } 1.434 + 1.435 + /* 1.436 + * This counts the ShapeTable object itself (which must be 1.437 + * heap-allocated) and its |entries| array. 1.438 + */ 1.439 + size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 1.440 + return mallocSizeOf(this) + mallocSizeOf(entries); 1.441 + } 1.442 + 1.443 + /* Whether we need to grow. We want to do this if the load factor is >= 0.75 */ 1.444 + bool needsToGrow() const { 1.445 + uint32_t size = capacity(); 1.446 + return entryCount + removedCount >= size - (size >> 2); 1.447 + } 1.448 + 1.449 + /* 1.450 + * Try to grow the table. On failure, reports out of memory on cx 1.451 + * and returns false. This will make any extant pointers into the 1.452 + * table invalid. Don't call this unless needsToGrow() is true. 1.453 + */ 1.454 + bool grow(ThreadSafeContext *cx); 1.455 + 1.456 + /* 1.457 + * NB: init and change are fallible but do not report OOM, so callers can 1.458 + * cope or ignore. They do however use the context's calloc_ method in 1.459 + * order to update the malloc counter on success. 1.460 + */ 1.461 + bool init(ThreadSafeContext *cx, Shape *lastProp); 1.462 + bool change(int log2Delta, ThreadSafeContext *cx); 1.463 + Shape **search(jsid id, bool adding); 1.464 +}; 1.465 + 1.466 +/* 1.467 + * Reuse the API-only JSPROP_INDEX attribute to mean shadowability. 1.468 + */ 1.469 +#define JSPROP_SHADOWABLE JSPROP_INDEX 1.470 + 1.471 +/* 1.472 + * Shapes encode information about both a property lineage *and* a particular 1.473 + * property. This information is split across the Shape and the BaseShape 1.474 + * at shape->base(). Both Shape and BaseShape can be either owned or unowned 1.475 + * by, respectively, the Object or Shape referring to them. 1.476 + * 1.477 + * Owned Shapes are used in dictionary objects, and form a doubly linked list 1.478 + * whose entries are all owned by that dictionary. Unowned Shapes are all in 1.479 + * the property tree. 1.480 + * 1.481 + * Owned BaseShapes are used for shapes which have shape tables, including 1.482 + * the last properties in all dictionaries. Unowned BaseShapes compactly store 1.483 + * information common to many shapes. In a given compartment there is a single 1.484 + * BaseShape for each combination of BaseShape information. This information 1.485 + * is cloned in owned BaseShapes so that information can be quickly looked up 1.486 + * for a given object or shape without regard to whether the base shape is 1.487 + * owned or not. 1.488 + * 1.489 + * All combinations of owned/unowned Shapes/BaseShapes are possible: 1.490 + * 1.491 + * Owned Shape, Owned BaseShape: 1.492 + * 1.493 + * Last property in a dictionary object. The BaseShape is transferred from 1.494 + * property to property as the object's last property changes. 1.495 + * 1.496 + * Owned Shape, Unowned BaseShape: 1.497 + * 1.498 + * Property in a dictionary object other than the last one. 1.499 + * 1.500 + * Unowned Shape, Owned BaseShape: 1.501 + * 1.502 + * Property in the property tree which has a shape table. 1.503 + * 1.504 + * Unowned Shape, Unowned BaseShape: 1.505 + * 1.506 + * Property in the property tree which does not have a shape table. 1.507 + * 1.508 + * BaseShapes additionally encode some information about the referring object 1.509 + * itself. This includes the object's class, parent and various flags that may 1.510 + * be set for the object. Except for the class, this information is mutable and 1.511 + * may change when the object has an established property lineage. On such 1.512 + * changes the entire property lineage is not updated, but rather only the 1.513 + * last property (and its base shape). This works because only the object's 1.514 + * last property is used to query information about the object. Care must be 1.515 + * taken to call JSObject::canRemoveLastProperty when unwinding an object to 1.516 + * an earlier property, however. 1.517 + */ 1.518 + 1.519 +class Shape; 1.520 +class UnownedBaseShape; 1.521 +struct StackBaseShape; 1.522 + 1.523 +namespace gc { 1.524 +void MergeCompartments(JSCompartment *source, JSCompartment *target); 1.525 +} 1.526 + 1.527 +static inline void 1.528 +GetterSetterWriteBarrierPost(JSRuntime *rt, JSObject **objp) 1.529 +{ 1.530 +#ifdef JSGC_GENERATIONAL 1.531 + JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt); 1.532 + shadowRuntime->gcStoreBufferPtr()->putRelocatableCell(reinterpret_cast<gc::Cell **>(objp)); 1.533 +#endif 1.534 +} 1.535 + 1.536 +static inline void 1.537 +GetterSetterWriteBarrierPostRemove(JSRuntime *rt, JSObject **objp) 1.538 +{ 1.539 +#ifdef JSGC_GENERATIONAL 1.540 + JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt); 1.541 + shadowRuntime->gcStoreBufferPtr()->removeRelocatableCell(reinterpret_cast<gc::Cell **>(objp)); 1.542 +#endif 1.543 +} 1.544 + 1.545 +class BaseShape : public gc::BarrieredCell<BaseShape> 1.546 +{ 1.547 + public: 1.548 + friend class Shape; 1.549 + friend struct StackBaseShape; 1.550 + friend struct StackShape; 1.551 + friend void gc::MergeCompartments(JSCompartment *source, JSCompartment *target); 1.552 + 1.553 + enum Flag { 1.554 + /* Owned by the referring shape. */ 1.555 + OWNED_SHAPE = 0x1, 1.556 + 1.557 + /* getterObj/setterObj are active in unions below. */ 1.558 + HAS_GETTER_OBJECT = 0x2, 1.559 + HAS_SETTER_OBJECT = 0x4, 1.560 + 1.561 + /* 1.562 + * Flags set which describe the referring object. Once set these cannot 1.563 + * be unset (except during object densification of sparse indexes), and 1.564 + * are transferred from shape to shape as the object's last property 1.565 + * changes. 1.566 + * 1.567 + * If you add a new flag here, please add appropriate code to 1.568 + * JSObject::dump to dump it as part of object representation. 1.569 + */ 1.570 + 1.571 + DELEGATE = 0x8, 1.572 + NOT_EXTENSIBLE = 0x10, 1.573 + INDEXED = 0x20, 1.574 + BOUND_FUNCTION = 0x40, 1.575 + VAROBJ = 0x80, 1.576 + WATCHED = 0x100, 1.577 + ITERATED_SINGLETON = 0x200, 1.578 + NEW_TYPE_UNKNOWN = 0x400, 1.579 + UNCACHEABLE_PROTO = 0x800, 1.580 + HAD_ELEMENTS_ACCESS = 0x1000, 1.581 + 1.582 + OBJECT_FLAG_MASK = 0x1ff8 1.583 + }; 1.584 + 1.585 + private: 1.586 + const Class *clasp_; /* Class of referring object. */ 1.587 + HeapPtrObject parent; /* Parent of referring object. */ 1.588 + HeapPtrObject metadata; /* Optional holder of metadata about 1.589 + * the referring object. */ 1.590 + JSCompartment *compartment_; /* Compartment shape belongs to. */ 1.591 + uint32_t flags; /* Vector of above flags. */ 1.592 + uint32_t slotSpan_; /* Object slot span for BaseShapes at 1.593 + * dictionary last properties. */ 1.594 + 1.595 + union { 1.596 + PropertyOp rawGetter; /* getter hook for shape */ 1.597 + JSObject *getterObj; /* user-defined callable "get" object or 1.598 + null if shape->hasGetterValue() */ 1.599 + }; 1.600 + 1.601 + union { 1.602 + StrictPropertyOp rawSetter; /* setter hook for shape */ 1.603 + JSObject *setterObj; /* user-defined callable "set" object or 1.604 + null if shape->hasSetterValue() */ 1.605 + }; 1.606 + 1.607 + /* For owned BaseShapes, the canonical unowned BaseShape. */ 1.608 + HeapPtr<UnownedBaseShape> unowned_; 1.609 + 1.610 + /* For owned BaseShapes, the shape's shape table. */ 1.611 + ShapeTable *table_; 1.612 + 1.613 + BaseShape(const BaseShape &base) MOZ_DELETE; 1.614 + 1.615 + public: 1.616 + void finalize(FreeOp *fop); 1.617 + 1.618 + BaseShape(JSCompartment *comp, const Class *clasp, JSObject *parent, JSObject *metadata, 1.619 + uint32_t objectFlags) 1.620 + { 1.621 + JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK)); 1.622 + mozilla::PodZero(this); 1.623 + this->clasp_ = clasp; 1.624 + this->parent = parent; 1.625 + this->metadata = metadata; 1.626 + this->flags = objectFlags; 1.627 + this->compartment_ = comp; 1.628 + } 1.629 + 1.630 + BaseShape(JSCompartment *comp, const Class *clasp, JSObject *parent, JSObject *metadata, 1.631 + uint32_t objectFlags, uint8_t attrs, 1.632 + PropertyOp rawGetter, StrictPropertyOp rawSetter) 1.633 + { 1.634 + JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK)); 1.635 + mozilla::PodZero(this); 1.636 + this->clasp_ = clasp; 1.637 + this->parent = parent; 1.638 + this->metadata = metadata; 1.639 + this->flags = objectFlags; 1.640 + this->rawGetter = rawGetter; 1.641 + this->rawSetter = rawSetter; 1.642 + if ((attrs & JSPROP_GETTER) && rawGetter) { 1.643 + this->flags |= HAS_GETTER_OBJECT; 1.644 + GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->getterObj); 1.645 + } 1.646 + if ((attrs & JSPROP_SETTER) && rawSetter) { 1.647 + this->flags |= HAS_SETTER_OBJECT; 1.648 + GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->setterObj); 1.649 + } 1.650 + this->compartment_ = comp; 1.651 + } 1.652 + 1.653 + inline BaseShape(const StackBaseShape &base); 1.654 + 1.655 + /* Not defined: BaseShapes must not be stack allocated. */ 1.656 + ~BaseShape(); 1.657 + 1.658 + BaseShape &operator=(const BaseShape &other) { 1.659 + clasp_ = other.clasp_; 1.660 + parent = other.parent; 1.661 + metadata = other.metadata; 1.662 + flags = other.flags; 1.663 + slotSpan_ = other.slotSpan_; 1.664 + if (flags & HAS_GETTER_OBJECT) { 1.665 + getterObj = other.getterObj; 1.666 + GetterSetterWriteBarrierPost(runtimeFromMainThread(), &getterObj); 1.667 + } else { 1.668 + if (rawGetter) 1.669 + GetterSetterWriteBarrierPostRemove(runtimeFromMainThread(), &getterObj); 1.670 + rawGetter = other.rawGetter; 1.671 + } 1.672 + if (flags & HAS_SETTER_OBJECT) { 1.673 + setterObj = other.setterObj; 1.674 + GetterSetterWriteBarrierPost(runtimeFromMainThread(), &setterObj); 1.675 + } else { 1.676 + if (rawSetter) 1.677 + GetterSetterWriteBarrierPostRemove(runtimeFromMainThread(), &setterObj); 1.678 + rawSetter = other.rawSetter; 1.679 + } 1.680 + compartment_ = other.compartment_; 1.681 + return *this; 1.682 + } 1.683 + 1.684 + const Class *clasp() const { return clasp_; } 1.685 + 1.686 + bool isOwned() const { return !!(flags & OWNED_SHAPE); } 1.687 + 1.688 + bool matchesGetterSetter(PropertyOp rawGetter, StrictPropertyOp rawSetter) const { 1.689 + return rawGetter == this->rawGetter && rawSetter == this->rawSetter; 1.690 + } 1.691 + 1.692 + inline void adoptUnowned(UnownedBaseShape *other); 1.693 + 1.694 + void setOwned(UnownedBaseShape *unowned) { 1.695 + flags |= OWNED_SHAPE; 1.696 + this->unowned_ = unowned; 1.697 + } 1.698 + 1.699 + JSObject *getObjectParent() const { return parent; } 1.700 + JSObject *getObjectMetadata() const { return metadata; } 1.701 + uint32_t getObjectFlags() const { return flags & OBJECT_FLAG_MASK; } 1.702 + 1.703 + bool hasGetterObject() const { return !!(flags & HAS_GETTER_OBJECT); } 1.704 + JSObject *getterObject() const { JS_ASSERT(hasGetterObject()); return getterObj; } 1.705 + 1.706 + bool hasSetterObject() const { return !!(flags & HAS_SETTER_OBJECT); } 1.707 + JSObject *setterObject() const { JS_ASSERT(hasSetterObject()); return setterObj; } 1.708 + 1.709 + bool hasTable() const { JS_ASSERT_IF(table_, isOwned()); return table_ != nullptr; } 1.710 + ShapeTable &table() const { JS_ASSERT(table_ && isOwned()); return *table_; } 1.711 + void setTable(ShapeTable *table) { JS_ASSERT(isOwned()); table_ = table; } 1.712 + 1.713 + uint32_t slotSpan() const { JS_ASSERT(isOwned()); return slotSpan_; } 1.714 + void setSlotSpan(uint32_t slotSpan) { JS_ASSERT(isOwned()); slotSpan_ = slotSpan; } 1.715 + 1.716 + JSCompartment *compartment() const { return compartment_; } 1.717 + 1.718 + /* 1.719 + * Lookup base shapes from the compartment's baseShapes table, adding if 1.720 + * not already found. 1.721 + */ 1.722 + static UnownedBaseShape* getUnowned(ExclusiveContext *cx, StackBaseShape &base); 1.723 + 1.724 + /* 1.725 + * Lookup base shapes from the compartment's baseShapes table, returning 1.726 + * nullptr if not found. 1.727 + */ 1.728 + static UnownedBaseShape *lookupUnowned(ThreadSafeContext *cx, const StackBaseShape &base); 1.729 + 1.730 + /* Get the canonical base shape. */ 1.731 + inline UnownedBaseShape* unowned(); 1.732 + 1.733 + /* Get the canonical base shape for an owned one. */ 1.734 + inline UnownedBaseShape* baseUnowned(); 1.735 + 1.736 + /* Get the canonical base shape for an unowned one (i.e. identity). */ 1.737 + inline UnownedBaseShape* toUnowned(); 1.738 + 1.739 + /* Check that an owned base shape is consistent with its unowned base. */ 1.740 + void assertConsistency(); 1.741 + 1.742 + /* For JIT usage */ 1.743 + static inline size_t offsetOfParent() { return offsetof(BaseShape, parent); } 1.744 + static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); } 1.745 + 1.746 + static inline ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; } 1.747 + 1.748 + void markChildren(JSTracer *trc) { 1.749 + if (hasGetterObject()) 1.750 + gc::MarkObjectUnbarriered(trc, &getterObj, "getter"); 1.751 + 1.752 + if (hasSetterObject()) 1.753 + gc::MarkObjectUnbarriered(trc, &setterObj, "setter"); 1.754 + 1.755 + if (isOwned()) 1.756 + gc::MarkBaseShape(trc, &unowned_, "base"); 1.757 + 1.758 + if (parent) 1.759 + gc::MarkObject(trc, &parent, "parent"); 1.760 + 1.761 + if (metadata) 1.762 + gc::MarkObject(trc, &metadata, "metadata"); 1.763 + } 1.764 + 1.765 + private: 1.766 + static void staticAsserts() { 1.767 + JS_STATIC_ASSERT(offsetof(BaseShape, clasp_) == offsetof(js::shadow::BaseShape, clasp_)); 1.768 + } 1.769 +}; 1.770 + 1.771 +class UnownedBaseShape : public BaseShape {}; 1.772 + 1.773 +inline void 1.774 +BaseShape::adoptUnowned(UnownedBaseShape *other) 1.775 +{ 1.776 + // This is a base shape owned by a dictionary object, update it to reflect the 1.777 + // unowned base shape of a new last property. 1.778 + JS_ASSERT(isOwned()); 1.779 + 1.780 + uint32_t span = slotSpan(); 1.781 + ShapeTable *table = &this->table(); 1.782 + 1.783 + *this = *other; 1.784 + setOwned(other); 1.785 + setTable(table); 1.786 + setSlotSpan(span); 1.787 + 1.788 + assertConsistency(); 1.789 +} 1.790 + 1.791 +UnownedBaseShape * 1.792 +BaseShape::unowned() 1.793 +{ 1.794 + return isOwned() ? baseUnowned() : toUnowned(); 1.795 +} 1.796 + 1.797 +UnownedBaseShape * 1.798 +BaseShape::toUnowned() 1.799 +{ 1.800 + JS_ASSERT(!isOwned() && !unowned_); return static_cast<UnownedBaseShape *>(this); 1.801 +} 1.802 + 1.803 +UnownedBaseShape* 1.804 +BaseShape::baseUnowned() 1.805 +{ 1.806 + JS_ASSERT(isOwned() && unowned_); return unowned_; 1.807 +} 1.808 + 1.809 +/* Entries for the per-compartment baseShapes set of unowned base shapes. */ 1.810 +struct StackBaseShape 1.811 +{ 1.812 + typedef const StackBaseShape *Lookup; 1.813 + 1.814 + uint32_t flags; 1.815 + const Class *clasp; 1.816 + JSObject *parent; 1.817 + JSObject *metadata; 1.818 + PropertyOp rawGetter; 1.819 + StrictPropertyOp rawSetter; 1.820 + JSCompartment *compartment; 1.821 + 1.822 + explicit StackBaseShape(BaseShape *base) 1.823 + : flags(base->flags & BaseShape::OBJECT_FLAG_MASK), 1.824 + clasp(base->clasp_), 1.825 + parent(base->parent), 1.826 + metadata(base->metadata), 1.827 + rawGetter(nullptr), 1.828 + rawSetter(nullptr), 1.829 + compartment(base->compartment()) 1.830 + {} 1.831 + 1.832 + inline StackBaseShape(ThreadSafeContext *cx, const Class *clasp, 1.833 + JSObject *parent, JSObject *metadata, uint32_t objectFlags); 1.834 + inline StackBaseShape(Shape *shape); 1.835 + 1.836 + void updateGetterSetter(uint8_t attrs, PropertyOp rawGetter, StrictPropertyOp rawSetter) { 1.837 + flags &= ~(BaseShape::HAS_GETTER_OBJECT | BaseShape::HAS_SETTER_OBJECT); 1.838 + if ((attrs & JSPROP_GETTER) && rawGetter) { 1.839 + JS_ASSERT(!IsPoisonedPtr(rawGetter)); 1.840 + flags |= BaseShape::HAS_GETTER_OBJECT; 1.841 + } 1.842 + if ((attrs & JSPROP_SETTER) && rawSetter) { 1.843 + JS_ASSERT(!IsPoisonedPtr(rawSetter)); 1.844 + flags |= BaseShape::HAS_SETTER_OBJECT; 1.845 + } 1.846 + 1.847 + this->rawGetter = rawGetter; 1.848 + this->rawSetter = rawSetter; 1.849 + } 1.850 + 1.851 + static inline HashNumber hash(const StackBaseShape *lookup); 1.852 + static inline bool match(UnownedBaseShape *key, const StackBaseShape *lookup); 1.853 + 1.854 + // For RootedGeneric<StackBaseShape*> 1.855 + static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; } 1.856 + void trace(JSTracer *trc); 1.857 +}; 1.858 + 1.859 +inline 1.860 +BaseShape::BaseShape(const StackBaseShape &base) 1.861 +{ 1.862 + mozilla::PodZero(this); 1.863 + this->clasp_ = base.clasp; 1.864 + this->parent = base.parent; 1.865 + this->metadata = base.metadata; 1.866 + this->flags = base.flags; 1.867 + this->rawGetter = base.rawGetter; 1.868 + this->rawSetter = base.rawSetter; 1.869 + if ((base.flags & HAS_GETTER_OBJECT) && base.rawGetter) 1.870 + GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->getterObj); 1.871 + if ((base.flags & HAS_SETTER_OBJECT) && base.rawSetter) 1.872 + GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->setterObj); 1.873 + this->compartment_ = base.compartment; 1.874 +} 1.875 + 1.876 +typedef HashSet<ReadBarriered<UnownedBaseShape>, 1.877 + StackBaseShape, 1.878 + SystemAllocPolicy> BaseShapeSet; 1.879 + 1.880 + 1.881 +class Shape : public gc::BarrieredCell<Shape> 1.882 +{ 1.883 + friend class ::JSObject; 1.884 + friend class ::JSFunction; 1.885 + friend class js::Bindings; 1.886 + friend class js::Nursery; 1.887 + friend class js::ObjectImpl; 1.888 + friend class js::PropertyTree; 1.889 + friend class js::StaticBlockObject; 1.890 + friend struct js::StackShape; 1.891 + friend struct js::StackBaseShape; 1.892 + 1.893 + protected: 1.894 + HeapPtrBaseShape base_; 1.895 + EncapsulatedId propid_; 1.896 + 1.897 + JS_ENUM_HEADER(SlotInfo, uint32_t) 1.898 + { 1.899 + /* Number of fixed slots in objects with this shape. */ 1.900 + // FIXED_SLOTS_MAX is the biggest count of fixed slots a Shape can store 1.901 + FIXED_SLOTS_MAX = 0x1f, 1.902 + FIXED_SLOTS_SHIFT = 27, 1.903 + FIXED_SLOTS_MASK = uint32_t(FIXED_SLOTS_MAX << FIXED_SLOTS_SHIFT), 1.904 + 1.905 + /* 1.906 + * numLinearSearches starts at zero and is incremented initially on 1.907 + * search() calls. Once numLinearSearches reaches LINEAR_SEARCHES_MAX, 1.908 + * the table is created on the next search() call. The table can also 1.909 + * be created when hashifying for dictionary mode. 1.910 + */ 1.911 + LINEAR_SEARCHES_MAX = 0x7, 1.912 + LINEAR_SEARCHES_SHIFT = 24, 1.913 + LINEAR_SEARCHES_MASK = LINEAR_SEARCHES_MAX << LINEAR_SEARCHES_SHIFT, 1.914 + 1.915 + /* 1.916 + * Mask to get the index in object slots for shapes which hasSlot(). 1.917 + * For !hasSlot() shapes in the property tree with a parent, stores the 1.918 + * parent's slot index (which may be invalid), and invalid for all 1.919 + * other shapes. 1.920 + */ 1.921 + SLOT_MASK = JS_BIT(24) - 1 1.922 + } JS_ENUM_FOOTER(SlotInfo); 1.923 + 1.924 + uint32_t slotInfo; /* mask of above info */ 1.925 + uint8_t attrs; /* attributes, see jsapi.h JSPROP_* */ 1.926 + uint8_t flags; /* flags, see below for defines */ 1.927 + 1.928 + HeapPtrShape parent; /* parent node, reverse for..in order */ 1.929 + /* kids is valid when !inDictionary(), listp is valid when inDictionary(). */ 1.930 + union { 1.931 + KidsPointer kids; /* null, single child, or a tagged ptr 1.932 + to many-kids data structure */ 1.933 + HeapPtrShape *listp; /* dictionary list starting at shape_ 1.934 + has a double-indirect back pointer, 1.935 + either to the next shape's parent if not 1.936 + last, else to obj->shape_ */ 1.937 + }; 1.938 + 1.939 + static inline Shape *search(ExclusiveContext *cx, Shape *start, jsid id, 1.940 + Shape ***pspp, bool adding = false); 1.941 + static inline Shape *searchThreadLocal(ThreadSafeContext *cx, Shape *start, jsid id, 1.942 + Shape ***pspp, bool adding = false); 1.943 + static inline Shape *searchNoHashify(Shape *start, jsid id); 1.944 + 1.945 + void removeFromDictionary(ObjectImpl *obj); 1.946 + void insertIntoDictionary(HeapPtrShape *dictp); 1.947 + 1.948 + void initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp) { 1.949 + new (this) Shape(child, nfixed); 1.950 + this->flags |= IN_DICTIONARY; 1.951 + 1.952 + this->listp = nullptr; 1.953 + insertIntoDictionary(dictp); 1.954 + } 1.955 + 1.956 + /* Replace the base shape of the last shape in a non-dictionary lineage with base. */ 1.957 + static Shape *replaceLastProperty(ExclusiveContext *cx, StackBaseShape &base, 1.958 + TaggedProto proto, HandleShape shape); 1.959 + 1.960 + /* 1.961 + * This function is thread safe if every shape in the lineage of |shape| 1.962 + * is thread local, which is the case when we clone the entire shape 1.963 + * lineage in preparation for converting an object to dictionary mode. 1.964 + */ 1.965 + static bool hashify(ThreadSafeContext *cx, Shape *shape); 1.966 + void handoffTableTo(Shape *newShape); 1.967 + 1.968 + void setParent(Shape *p) { 1.969 + JS_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(), 1.970 + p->maybeSlot() <= maybeSlot()); 1.971 + JS_ASSERT_IF(p && !inDictionary(), 1.972 + hasSlot() == (p->maybeSlot() != maybeSlot())); 1.973 + parent = p; 1.974 + } 1.975 + 1.976 + bool ensureOwnBaseShape(ThreadSafeContext *cx) { 1.977 + if (base()->isOwned()) 1.978 + return true; 1.979 + return makeOwnBaseShape(cx); 1.980 + } 1.981 + 1.982 + bool makeOwnBaseShape(ThreadSafeContext *cx); 1.983 + 1.984 + public: 1.985 + bool hasTable() const { return base()->hasTable(); } 1.986 + ShapeTable &table() const { return base()->table(); } 1.987 + 1.988 + void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, 1.989 + size_t *propTableSize, size_t *kidsSize) const { 1.990 + if (hasTable()) 1.991 + *propTableSize += table().sizeOfIncludingThis(mallocSizeOf); 1.992 + 1.993 + if (!inDictionary() && kids.isHash()) 1.994 + *kidsSize += kids.toHash()->sizeOfIncludingThis(mallocSizeOf); 1.995 + } 1.996 + 1.997 + bool isNative() const { 1.998 + JS_ASSERT(!(flags & NON_NATIVE) == getObjectClass()->isNative()); 1.999 + return !(flags & NON_NATIVE); 1.1000 + } 1.1001 + 1.1002 + const HeapPtrShape &previous() const { return parent; } 1.1003 + JSCompartment *compartment() const { return base()->compartment(); } 1.1004 + 1.1005 + template <AllowGC allowGC> 1.1006 + class Range { 1.1007 + protected: 1.1008 + friend class Shape; 1.1009 + 1.1010 + typename MaybeRooted<Shape*, allowGC>::RootType cursor; 1.1011 + 1.1012 + public: 1.1013 + Range(ExclusiveContext *cx, Shape *shape) : cursor(cx, shape) { 1.1014 + JS_STATIC_ASSERT(allowGC == CanGC); 1.1015 + } 1.1016 + 1.1017 + Range(Shape *shape) : cursor((ExclusiveContext *) nullptr, shape) { 1.1018 + JS_STATIC_ASSERT(allowGC == NoGC); 1.1019 + } 1.1020 + 1.1021 + bool empty() const { 1.1022 + return !cursor || cursor->isEmptyShape(); 1.1023 + } 1.1024 + 1.1025 + Shape &front() const { 1.1026 + JS_ASSERT(!empty()); 1.1027 + return *cursor; 1.1028 + } 1.1029 + 1.1030 + void popFront() { 1.1031 + JS_ASSERT(!empty()); 1.1032 + cursor = cursor->parent; 1.1033 + } 1.1034 + }; 1.1035 + 1.1036 + const Class *getObjectClass() const { 1.1037 + return base()->clasp_; 1.1038 + } 1.1039 + JSObject *getObjectParent() const { return base()->parent; } 1.1040 + JSObject *getObjectMetadata() const { return base()->metadata; } 1.1041 + 1.1042 + static Shape *setObjectParent(ExclusiveContext *cx, 1.1043 + JSObject *obj, TaggedProto proto, Shape *last); 1.1044 + static Shape *setObjectMetadata(JSContext *cx, 1.1045 + JSObject *metadata, TaggedProto proto, Shape *last); 1.1046 + static Shape *setObjectFlag(ExclusiveContext *cx, 1.1047 + BaseShape::Flag flag, TaggedProto proto, Shape *last); 1.1048 + 1.1049 + uint32_t getObjectFlags() const { return base()->getObjectFlags(); } 1.1050 + bool hasObjectFlag(BaseShape::Flag flag) const { 1.1051 + JS_ASSERT(!(flag & ~BaseShape::OBJECT_FLAG_MASK)); 1.1052 + return !!(base()->flags & flag); 1.1053 + } 1.1054 + 1.1055 + protected: 1.1056 + /* 1.1057 + * Implementation-private bits stored in shape->flags. See public: enum {} 1.1058 + * flags further below, which were allocated FCFS over time, so interleave 1.1059 + * with these bits. 1.1060 + */ 1.1061 + enum { 1.1062 + /* Property is placeholder for a non-native class. */ 1.1063 + NON_NATIVE = 0x01, 1.1064 + 1.1065 + /* Property stored in per-object dictionary, not shared property tree. */ 1.1066 + IN_DICTIONARY = 0x02, 1.1067 + 1.1068 + UNUSED_BITS = 0x3C 1.1069 + }; 1.1070 + 1.1071 + /* Get a shape identical to this one, without parent/kids information. */ 1.1072 + inline Shape(const StackShape &other, uint32_t nfixed); 1.1073 + 1.1074 + /* Used by EmptyShape (see jsscopeinlines.h). */ 1.1075 + inline Shape(UnownedBaseShape *base, uint32_t nfixed); 1.1076 + 1.1077 + /* Copy constructor disabled, to avoid misuse of the above form. */ 1.1078 + Shape(const Shape &other) MOZ_DELETE; 1.1079 + 1.1080 + /* 1.1081 + * Whether this shape has a valid slot value. This may be true even if 1.1082 + * !hasSlot() (see SlotInfo comment above), and may be false even if 1.1083 + * hasSlot() if the shape is being constructed and has not had a slot 1.1084 + * assigned yet. After construction, hasSlot() implies !hasMissingSlot(). 1.1085 + */ 1.1086 + bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; } 1.1087 + 1.1088 + public: 1.1089 + bool inDictionary() const { 1.1090 + return (flags & IN_DICTIONARY) != 0; 1.1091 + } 1.1092 + 1.1093 + PropertyOp getter() const { return base()->rawGetter; } 1.1094 + bool hasDefaultGetter() const {return !base()->rawGetter; } 1.1095 + PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return base()->rawGetter; } 1.1096 + JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return base()->getterObj; } 1.1097 + 1.1098 + // Per ES5, decode null getterObj as the undefined value, which encodes as null. 1.1099 + Value getterValue() const { 1.1100 + JS_ASSERT(hasGetterValue()); 1.1101 + return base()->getterObj ? ObjectValue(*base()->getterObj) : UndefinedValue(); 1.1102 + } 1.1103 + 1.1104 + Value getterOrUndefined() const { 1.1105 + return (hasGetterValue() && base()->getterObj) 1.1106 + ? ObjectValue(*base()->getterObj) 1.1107 + : UndefinedValue(); 1.1108 + } 1.1109 + 1.1110 + StrictPropertyOp setter() const { return base()->rawSetter; } 1.1111 + bool hasDefaultSetter() const { return !base()->rawSetter; } 1.1112 + StrictPropertyOp setterOp() const { JS_ASSERT(!hasSetterValue()); return base()->rawSetter; } 1.1113 + JSObject *setterObject() const { JS_ASSERT(hasSetterValue()); return base()->setterObj; } 1.1114 + 1.1115 + // Per ES5, decode null setterObj as the undefined value, which encodes as null. 1.1116 + Value setterValue() const { 1.1117 + JS_ASSERT(hasSetterValue()); 1.1118 + return base()->setterObj ? ObjectValue(*base()->setterObj) : UndefinedValue(); 1.1119 + } 1.1120 + 1.1121 + Value setterOrUndefined() const { 1.1122 + return (hasSetterValue() && base()->setterObj) 1.1123 + ? ObjectValue(*base()->setterObj) 1.1124 + : UndefinedValue(); 1.1125 + } 1.1126 + 1.1127 + void update(PropertyOp getter, StrictPropertyOp setter, uint8_t attrs); 1.1128 + 1.1129 + bool matches(const Shape *other) const { 1.1130 + return propid_.get() == other->propid_.get() && 1.1131 + matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs, other->flags); 1.1132 + } 1.1133 + 1.1134 + inline bool matches(const StackShape &other) const; 1.1135 + 1.1136 + bool matchesParamsAfterId(BaseShape *base, uint32_t aslot, unsigned aattrs, unsigned aflags) const 1.1137 + { 1.1138 + return base->unowned() == this->base()->unowned() && 1.1139 + maybeSlot() == aslot && 1.1140 + attrs == aattrs; 1.1141 + } 1.1142 + 1.1143 + bool get(JSContext* cx, HandleObject receiver, JSObject *obj, JSObject *pobj, MutableHandleValue vp); 1.1144 + bool set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, MutableHandleValue vp); 1.1145 + 1.1146 + BaseShape *base() const { return base_.get(); } 1.1147 + 1.1148 + bool hasSlot() const { 1.1149 + return (attrs & JSPROP_SHARED) == 0; 1.1150 + } 1.1151 + uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); } 1.1152 + uint32_t maybeSlot() const { 1.1153 + return slotInfo & SLOT_MASK; 1.1154 + } 1.1155 + 1.1156 + bool isEmptyShape() const { 1.1157 + JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot()); 1.1158 + return JSID_IS_EMPTY(propid_); 1.1159 + } 1.1160 + 1.1161 + uint32_t slotSpan(const Class *clasp) const { 1.1162 + JS_ASSERT(!inDictionary()); 1.1163 + uint32_t free = JSSLOT_FREE(clasp); 1.1164 + return hasMissingSlot() ? free : Max(free, maybeSlot() + 1); 1.1165 + } 1.1166 + 1.1167 + uint32_t slotSpan() const { 1.1168 + return slotSpan(getObjectClass()); 1.1169 + } 1.1170 + 1.1171 + void setSlot(uint32_t slot) { 1.1172 + JS_ASSERT(slot <= SHAPE_INVALID_SLOT); 1.1173 + slotInfo = slotInfo & ~Shape::SLOT_MASK; 1.1174 + slotInfo = slotInfo | slot; 1.1175 + } 1.1176 + 1.1177 + uint32_t numFixedSlots() const { 1.1178 + return slotInfo >> FIXED_SLOTS_SHIFT; 1.1179 + } 1.1180 + 1.1181 + void setNumFixedSlots(uint32_t nfixed) { 1.1182 + JS_ASSERT(nfixed < FIXED_SLOTS_MAX); 1.1183 + slotInfo = slotInfo & ~FIXED_SLOTS_MASK; 1.1184 + slotInfo = slotInfo | (nfixed << FIXED_SLOTS_SHIFT); 1.1185 + } 1.1186 + 1.1187 + uint32_t numLinearSearches() const { 1.1188 + return (slotInfo & LINEAR_SEARCHES_MASK) >> LINEAR_SEARCHES_SHIFT; 1.1189 + } 1.1190 + 1.1191 + void incrementNumLinearSearches() { 1.1192 + uint32_t count = numLinearSearches(); 1.1193 + JS_ASSERT(count < LINEAR_SEARCHES_MAX); 1.1194 + slotInfo = slotInfo & ~LINEAR_SEARCHES_MASK; 1.1195 + slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT); 1.1196 + } 1.1197 + 1.1198 + const EncapsulatedId &propid() const { 1.1199 + JS_ASSERT(!isEmptyShape()); 1.1200 + JS_ASSERT(!JSID_IS_VOID(propid_)); 1.1201 + return propid_; 1.1202 + } 1.1203 + EncapsulatedId &propidRef() { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; } 1.1204 + jsid propidRaw() const { 1.1205 + // Return the actual jsid, not an internal reference. 1.1206 + return propid(); 1.1207 + } 1.1208 + 1.1209 + uint8_t attributes() const { return attrs; } 1.1210 + bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; } 1.1211 + bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; } 1.1212 + bool writable() const { 1.1213 + return (attrs & JSPROP_READONLY) == 0; 1.1214 + } 1.1215 + bool hasGetterValue() const { return attrs & JSPROP_GETTER; } 1.1216 + bool hasSetterValue() const { return attrs & JSPROP_SETTER; } 1.1217 + 1.1218 + bool isDataDescriptor() const { 1.1219 + return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0; 1.1220 + } 1.1221 + bool isAccessorDescriptor() const { 1.1222 + return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0; 1.1223 + } 1.1224 + 1.1225 + PropDesc::Writability writability() const { 1.1226 + return (attrs & JSPROP_READONLY) ? PropDesc::NonWritable : PropDesc::Writable; 1.1227 + } 1.1228 + PropDesc::Enumerability enumerability() const { 1.1229 + return (attrs & JSPROP_ENUMERATE) ? PropDesc::Enumerable : PropDesc::NonEnumerable; 1.1230 + } 1.1231 + PropDesc::Configurability configurability() const { 1.1232 + return (attrs & JSPROP_PERMANENT) ? PropDesc::NonConfigurable : PropDesc::Configurable; 1.1233 + } 1.1234 + 1.1235 + /* 1.1236 + * For ES5 compatibility, we allow properties with PropertyOp-flavored 1.1237 + * setters to be shadowed when set. The "own" property thereby created in 1.1238 + * the directly referenced object will have the same getter and setter as 1.1239 + * the prototype property. See bug 552432. 1.1240 + */ 1.1241 + bool shadowable() const { 1.1242 + JS_ASSERT_IF(isDataDescriptor(), writable()); 1.1243 + return hasSlot() || (attrs & JSPROP_SHADOWABLE); 1.1244 + } 1.1245 + 1.1246 + uint32_t entryCount() { 1.1247 + if (hasTable()) 1.1248 + return table().entryCount; 1.1249 + uint32_t count = 0; 1.1250 + for (Shape::Range<NoGC> r(this); !r.empty(); r.popFront()) 1.1251 + ++count; 1.1252 + return count; 1.1253 + } 1.1254 + 1.1255 + bool isBigEnoughForAShapeTable() { 1.1256 + JS_ASSERT(!hasTable()); 1.1257 + Shape *shape = this; 1.1258 + uint32_t count = 0; 1.1259 + for (Shape::Range<NoGC> r(shape); !r.empty(); r.popFront()) { 1.1260 + ++count; 1.1261 + if (count >= ShapeTable::MIN_ENTRIES) 1.1262 + return true; 1.1263 + } 1.1264 + return false; 1.1265 + } 1.1266 + 1.1267 +#ifdef DEBUG 1.1268 + void dump(JSContext *cx, FILE *fp) const; 1.1269 + void dumpSubtree(JSContext *cx, int level, FILE *fp) const; 1.1270 +#endif 1.1271 + 1.1272 + void sweep(); 1.1273 + void finalize(FreeOp *fop); 1.1274 + void removeChild(Shape *child); 1.1275 + 1.1276 + static inline ThingRootKind rootKind() { return THING_ROOT_SHAPE; } 1.1277 + 1.1278 + void markChildren(JSTracer *trc) { 1.1279 + MarkBaseShape(trc, &base_, "base"); 1.1280 + gc::MarkId(trc, &propidRef(), "propid"); 1.1281 + if (parent) 1.1282 + MarkShape(trc, &parent, "parent"); 1.1283 + } 1.1284 + 1.1285 + inline Shape *search(ExclusiveContext *cx, jsid id); 1.1286 + inline Shape *searchLinear(jsid id); 1.1287 + 1.1288 + /* For JIT usage */ 1.1289 + static inline size_t offsetOfBase() { return offsetof(Shape, base_); } 1.1290 + 1.1291 + private: 1.1292 + static void staticAsserts() { 1.1293 + JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base)); 1.1294 + JS_STATIC_ASSERT(offsetof(Shape, slotInfo) == offsetof(js::shadow::Shape, slotInfo)); 1.1295 + JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Shape::FIXED_SLOTS_SHIFT); 1.1296 + static_assert(js::shadow::Object::MAX_FIXED_SLOTS <= FIXED_SLOTS_MAX, 1.1297 + "verify numFixedSlots() bitfield is big enough"); 1.1298 + } 1.1299 +}; 1.1300 + 1.1301 +inline 1.1302 +StackBaseShape::StackBaseShape(Shape *shape) 1.1303 + : flags(shape->getObjectFlags()), 1.1304 + clasp(shape->getObjectClass()), 1.1305 + parent(shape->getObjectParent()), 1.1306 + metadata(shape->getObjectMetadata()), 1.1307 + compartment(shape->compartment()) 1.1308 +{ 1.1309 + updateGetterSetter(shape->attrs, shape->getter(), shape->setter()); 1.1310 +} 1.1311 + 1.1312 +class AutoRooterGetterSetter 1.1313 +{ 1.1314 + class Inner : private JS::CustomAutoRooter 1.1315 + { 1.1316 + public: 1.1317 + inline Inner(ThreadSafeContext *cx, uint8_t attrs, 1.1318 + PropertyOp *pgetter_, StrictPropertyOp *psetter_); 1.1319 + 1.1320 + private: 1.1321 + virtual void trace(JSTracer *trc); 1.1322 + 1.1323 + uint8_t attrs; 1.1324 + PropertyOp *pgetter; 1.1325 + StrictPropertyOp *psetter; 1.1326 + }; 1.1327 + 1.1328 + public: 1.1329 + inline AutoRooterGetterSetter(ThreadSafeContext *cx, uint8_t attrs, 1.1330 + PropertyOp *pgetter, StrictPropertyOp *psetter 1.1331 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM); 1.1332 + 1.1333 + private: 1.1334 + mozilla::Maybe<Inner> inner; 1.1335 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.1336 +}; 1.1337 + 1.1338 +struct EmptyShape : public js::Shape 1.1339 +{ 1.1340 + EmptyShape(UnownedBaseShape *base, uint32_t nfixed) 1.1341 + : js::Shape(base, nfixed) 1.1342 + { 1.1343 + // Only empty shapes can be NON_NATIVE. 1.1344 + if (!getObjectClass()->isNative()) 1.1345 + flags |= NON_NATIVE; 1.1346 + } 1.1347 + 1.1348 + /* 1.1349 + * Lookup an initial shape matching the given parameters, creating an empty 1.1350 + * shape if none was found. 1.1351 + */ 1.1352 + static Shape *getInitialShape(ExclusiveContext *cx, const Class *clasp, 1.1353 + TaggedProto proto, JSObject *metadata, 1.1354 + JSObject *parent, size_t nfixed, uint32_t objectFlags = 0); 1.1355 + static Shape *getInitialShape(ExclusiveContext *cx, const Class *clasp, 1.1356 + TaggedProto proto, JSObject *metadata, 1.1357 + JSObject *parent, gc::AllocKind kind, uint32_t objectFlags = 0); 1.1358 + 1.1359 + /* 1.1360 + * Reinsert an alternate initial shape, to be returned by future 1.1361 + * getInitialShape calls, until the new shape becomes unreachable in a GC 1.1362 + * and the table entry is purged. 1.1363 + */ 1.1364 + static void insertInitialShape(ExclusiveContext *cx, HandleShape shape, HandleObject proto); 1.1365 + 1.1366 + /* 1.1367 + * Some object subclasses are allocated with a built-in set of properties. 1.1368 + * The first time such an object is created, these built-in properties must 1.1369 + * be set manually, to compute an initial shape. Afterward, that initial 1.1370 + * shape can be reused for newly-created objects that use the subclass's 1.1371 + * standard prototype. This method should be used in a post-allocation 1.1372 + * init method, to ensure that objects of such subclasses compute and cache 1.1373 + * the initial shape, if it hasn't already been computed. 1.1374 + */ 1.1375 + template<class ObjectSubclass> 1.1376 + static inline bool 1.1377 + ensureInitialCustomShape(ExclusiveContext *cx, Handle<ObjectSubclass*> obj); 1.1378 +}; 1.1379 + 1.1380 +/* 1.1381 + * Entries for the per-compartment initialShapes set indexing initial shapes 1.1382 + * for objects in the compartment and the associated types. 1.1383 + */ 1.1384 +struct InitialShapeEntry 1.1385 +{ 1.1386 + /* 1.1387 + * Initial shape to give to the object. This is an empty shape, except for 1.1388 + * certain classes (e.g. String, RegExp) which may add certain baked-in 1.1389 + * properties. 1.1390 + */ 1.1391 + ReadBarriered<Shape> shape; 1.1392 + 1.1393 + /* 1.1394 + * Matching prototype for the entry. The shape of an object determines its 1.1395 + * prototype, but the prototype cannot be determined from the shape itself. 1.1396 + */ 1.1397 + TaggedProto proto; 1.1398 + 1.1399 + /* State used to determine a match on an initial shape. */ 1.1400 + struct Lookup { 1.1401 + const Class *clasp; 1.1402 + TaggedProto hashProto; 1.1403 + TaggedProto matchProto; 1.1404 + JSObject *hashParent; 1.1405 + JSObject *matchParent; 1.1406 + JSObject *hashMetadata; 1.1407 + JSObject *matchMetadata; 1.1408 + uint32_t nfixed; 1.1409 + uint32_t baseFlags; 1.1410 + Lookup(const Class *clasp, TaggedProto proto, JSObject *parent, JSObject *metadata, 1.1411 + uint32_t nfixed, uint32_t baseFlags) 1.1412 + : clasp(clasp), 1.1413 + hashProto(proto), matchProto(proto), 1.1414 + hashParent(parent), matchParent(parent), 1.1415 + hashMetadata(metadata), matchMetadata(metadata), 1.1416 + nfixed(nfixed), baseFlags(baseFlags) 1.1417 + {} 1.1418 + 1.1419 +#ifdef JSGC_GENERATIONAL 1.1420 + /* 1.1421 + * For use by generational GC post barriers. Look up an entry whose 1.1422 + * parent and metadata fields may have been moved, but was hashed with 1.1423 + * the original values. 1.1424 + */ 1.1425 + Lookup(const Class *clasp, TaggedProto proto, 1.1426 + JSObject *hashParent, JSObject *matchParent, 1.1427 + JSObject *hashMetadata, JSObject *matchMetadata, 1.1428 + uint32_t nfixed, uint32_t baseFlags) 1.1429 + : clasp(clasp), 1.1430 + hashProto(proto), matchProto(proto), 1.1431 + hashParent(hashParent), matchParent(matchParent), 1.1432 + hashMetadata(hashMetadata), matchMetadata(matchMetadata), 1.1433 + nfixed(nfixed), baseFlags(baseFlags) 1.1434 + {} 1.1435 +#endif 1.1436 + }; 1.1437 + 1.1438 + inline InitialShapeEntry(); 1.1439 + inline InitialShapeEntry(const ReadBarriered<Shape> &shape, TaggedProto proto); 1.1440 + 1.1441 + inline Lookup getLookup() const; 1.1442 + 1.1443 + static inline HashNumber hash(const Lookup &lookup); 1.1444 + static inline bool match(const InitialShapeEntry &key, const Lookup &lookup); 1.1445 + static void rekey(InitialShapeEntry &k, const InitialShapeEntry& newKey) { k = newKey; } 1.1446 +}; 1.1447 + 1.1448 +typedef HashSet<InitialShapeEntry, InitialShapeEntry, SystemAllocPolicy> InitialShapeSet; 1.1449 + 1.1450 +struct StackShape 1.1451 +{ 1.1452 + /* For performance, StackShape only roots when absolutely necessary. */ 1.1453 + UnownedBaseShape *base; 1.1454 + jsid propid; 1.1455 + uint32_t slot_; 1.1456 + uint8_t attrs; 1.1457 + uint8_t flags; 1.1458 + 1.1459 + explicit StackShape(UnownedBaseShape *base, jsid propid, uint32_t slot, 1.1460 + unsigned attrs, unsigned flags) 1.1461 + : base(base), 1.1462 + propid(propid), 1.1463 + slot_(slot), 1.1464 + attrs(uint8_t(attrs)), 1.1465 + flags(uint8_t(flags)) 1.1466 + { 1.1467 + JS_ASSERT(base); 1.1468 + JS_ASSERT(!JSID_IS_VOID(propid)); 1.1469 + JS_ASSERT(slot <= SHAPE_INVALID_SLOT); 1.1470 + JS_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED); 1.1471 + } 1.1472 + 1.1473 + StackShape(Shape *shape) 1.1474 + : base(shape->base()->unowned()), 1.1475 + propid(shape->propidRef()), 1.1476 + slot_(shape->maybeSlot()), 1.1477 + attrs(shape->attrs), 1.1478 + flags(shape->flags) 1.1479 + {} 1.1480 + 1.1481 + bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; } 1.1482 + bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; } 1.1483 + 1.1484 + uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; } 1.1485 + uint32_t maybeSlot() const { return slot_; } 1.1486 + 1.1487 + uint32_t slotSpan() const { 1.1488 + uint32_t free = JSSLOT_FREE(base->clasp_); 1.1489 + return hasMissingSlot() ? free : (maybeSlot() + 1); 1.1490 + } 1.1491 + 1.1492 + void setSlot(uint32_t slot) { 1.1493 + JS_ASSERT(slot <= SHAPE_INVALID_SLOT); 1.1494 + slot_ = slot; 1.1495 + } 1.1496 + 1.1497 + HashNumber hash() const { 1.1498 + HashNumber hash = uintptr_t(base); 1.1499 + 1.1500 + /* Accumulate from least to most random so the low bits are most random. */ 1.1501 + hash = mozilla::RotateLeft(hash, 4) ^ attrs; 1.1502 + hash = mozilla::RotateLeft(hash, 4) ^ slot_; 1.1503 + hash = mozilla::RotateLeft(hash, 4) ^ JSID_BITS(propid); 1.1504 + return hash; 1.1505 + } 1.1506 + 1.1507 + // For RootedGeneric<StackShape*> 1.1508 + static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; } 1.1509 + void trace(JSTracer *trc); 1.1510 +}; 1.1511 + 1.1512 +} /* namespace js */ 1.1513 + 1.1514 +/* js::Shape pointer tag bit indicating a collision. */ 1.1515 +#define SHAPE_COLLISION (uintptr_t(1)) 1.1516 +#define SHAPE_REMOVED ((js::Shape *) SHAPE_COLLISION) 1.1517 + 1.1518 +/* Functions to get and set shape pointer values and collision flags. */ 1.1519 + 1.1520 +inline bool 1.1521 +SHAPE_IS_FREE(js::Shape *shape) 1.1522 +{ 1.1523 + return shape == nullptr; 1.1524 +} 1.1525 + 1.1526 +inline bool 1.1527 +SHAPE_IS_REMOVED(js::Shape *shape) 1.1528 +{ 1.1529 + return shape == SHAPE_REMOVED; 1.1530 +} 1.1531 + 1.1532 +inline bool 1.1533 +SHAPE_IS_LIVE(js::Shape *shape) 1.1534 +{ 1.1535 + return shape > SHAPE_REMOVED; 1.1536 +} 1.1537 + 1.1538 +inline void 1.1539 +SHAPE_FLAG_COLLISION(js::Shape **spp, js::Shape *shape) 1.1540 +{ 1.1541 + *spp = reinterpret_cast<js::Shape*>(uintptr_t(shape) | SHAPE_COLLISION); 1.1542 +} 1.1543 + 1.1544 +inline bool 1.1545 +SHAPE_HAD_COLLISION(js::Shape *shape) 1.1546 +{ 1.1547 + return uintptr_t(shape) & SHAPE_COLLISION; 1.1548 +} 1.1549 + 1.1550 +inline js::Shape * 1.1551 +SHAPE_CLEAR_COLLISION(js::Shape *shape) 1.1552 +{ 1.1553 + return reinterpret_cast<js::Shape*>(uintptr_t(shape) & ~SHAPE_COLLISION); 1.1554 +} 1.1555 + 1.1556 +inline js::Shape * 1.1557 +SHAPE_FETCH(js::Shape **spp) 1.1558 +{ 1.1559 + return SHAPE_CLEAR_COLLISION(*spp); 1.1560 +} 1.1561 + 1.1562 +inline void 1.1563 +SHAPE_STORE_PRESERVING_COLLISION(js::Shape **spp, js::Shape *shape) 1.1564 +{ 1.1565 + *spp = reinterpret_cast<js::Shape*>(uintptr_t(shape) | 1.1566 + uintptr_t(SHAPE_HAD_COLLISION(*spp))); 1.1567 +} 1.1568 + 1.1569 +namespace js { 1.1570 + 1.1571 +inline 1.1572 +Shape::Shape(const StackShape &other, uint32_t nfixed) 1.1573 + : base_(other.base), 1.1574 + propid_(other.propid), 1.1575 + slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)), 1.1576 + attrs(other.attrs), 1.1577 + flags(other.flags), 1.1578 + parent(nullptr) 1.1579 +{ 1.1580 + JS_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED); 1.1581 + kids.setNull(); 1.1582 +} 1.1583 + 1.1584 +inline 1.1585 +Shape::Shape(UnownedBaseShape *base, uint32_t nfixed) 1.1586 + : base_(base), 1.1587 + propid_(JSID_EMPTY), 1.1588 + slotInfo(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)), 1.1589 + attrs(JSPROP_SHARED), 1.1590 + flags(0), 1.1591 + parent(nullptr) 1.1592 +{ 1.1593 + JS_ASSERT(base); 1.1594 + kids.setNull(); 1.1595 +} 1.1596 + 1.1597 +inline Shape * 1.1598 +Shape::searchLinear(jsid id) 1.1599 +{ 1.1600 + /* 1.1601 + * Non-dictionary shapes can acquire a table at any point the main thread 1.1602 + * is operating on it, so other threads inspecting such shapes can't use 1.1603 + * their table without racing. This function can be called from any thread 1.1604 + * on any non-dictionary shape. 1.1605 + */ 1.1606 + JS_ASSERT(!inDictionary()); 1.1607 + 1.1608 + for (Shape *shape = this; shape; ) { 1.1609 + if (shape->propidRef() == id) 1.1610 + return shape; 1.1611 + shape = shape->parent; 1.1612 + } 1.1613 + 1.1614 + return nullptr; 1.1615 +} 1.1616 + 1.1617 +/* 1.1618 + * Keep this function in sync with search. It neither hashifies the start 1.1619 + * shape nor increments linear search count. 1.1620 + */ 1.1621 +inline Shape * 1.1622 +Shape::searchNoHashify(Shape *start, jsid id) 1.1623 +{ 1.1624 + /* 1.1625 + * If we have a table, search in the shape table, else do a linear 1.1626 + * search. We never hashify into a table in parallel. 1.1627 + */ 1.1628 + if (start->hasTable()) { 1.1629 + Shape **spp = start->table().search(id, false); 1.1630 + return SHAPE_FETCH(spp); 1.1631 + } 1.1632 + 1.1633 + return start->searchLinear(id); 1.1634 +} 1.1635 + 1.1636 +inline bool 1.1637 +Shape::matches(const StackShape &other) const 1.1638 +{ 1.1639 + return propid_.get() == other.propid && 1.1640 + matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags); 1.1641 +} 1.1642 + 1.1643 +template<> struct RootKind<Shape *> : SpecificRootKind<Shape *, THING_ROOT_SHAPE> {}; 1.1644 +template<> struct RootKind<BaseShape *> : SpecificRootKind<BaseShape *, THING_ROOT_BASE_SHAPE> {}; 1.1645 + 1.1646 +// Property lookup hooks on objects are required to return a non-nullptr shape 1.1647 +// to signify that the property has been found. For cases where the property is 1.1648 +// not actually represented by a Shape, use a dummy value. This includes all 1.1649 +// properties of non-native objects, and dense elements for native objects. 1.1650 +// Use separate APIs for these two cases. 1.1651 + 1.1652 +static inline void 1.1653 +MarkNonNativePropertyFound(MutableHandleShape propp) 1.1654 +{ 1.1655 + propp.set(reinterpret_cast<Shape*>(1)); 1.1656 +} 1.1657 + 1.1658 +template <AllowGC allowGC> 1.1659 +static inline void 1.1660 +MarkDenseOrTypedArrayElementFound(typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp) 1.1661 +{ 1.1662 + propp.set(reinterpret_cast<Shape*>(1)); 1.1663 +} 1.1664 + 1.1665 +static inline bool 1.1666 +IsImplicitDenseOrTypedArrayElement(Shape *prop) 1.1667 +{ 1.1668 + return prop == reinterpret_cast<Shape*>(1); 1.1669 +} 1.1670 + 1.1671 +} // namespace js 1.1672 + 1.1673 +#ifdef _MSC_VER 1.1674 +#pragma warning(pop) 1.1675 +#pragma warning(pop) 1.1676 +#endif 1.1677 + 1.1678 +namespace JS { 1.1679 +template<> class AnchorPermitted<js::Shape *> { }; 1.1680 +template<> class AnchorPermitted<const js::Shape *> { }; 1.1681 +} 1.1682 + 1.1683 +#endif /* vm_Shape_h */