1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/vm/ObjectImpl.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1014 @@ 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_ObjectImpl_h 1.11 +#define vm_ObjectImpl_h 1.12 + 1.13 +#include "mozilla/Assertions.h" 1.14 +#include "mozilla/Attributes.h" 1.15 + 1.16 +#include <stdint.h> 1.17 + 1.18 +#include "jsfriendapi.h" 1.19 +#include "jsinfer.h" 1.20 +#include "NamespaceImports.h" 1.21 + 1.22 +#include "gc/Barrier.h" 1.23 +#include "gc/Heap.h" 1.24 +#include "gc/Marking.h" 1.25 +#include "js/Value.h" 1.26 +#include "vm/NumericConversions.h" 1.27 +#include "vm/Shape.h" 1.28 +#include "vm/String.h" 1.29 + 1.30 +namespace js { 1.31 + 1.32 +class ObjectImpl; 1.33 +class Nursery; 1.34 +class Shape; 1.35 + 1.36 +/* 1.37 + * To really poison a set of values, using 'magic' or 'undefined' isn't good 1.38 + * enough since often these will just be ignored by buggy code (see bug 629974) 1.39 + * in debug builds and crash in release builds. Instead, we use a safe-for-crash 1.40 + * pointer. 1.41 + */ 1.42 +static MOZ_ALWAYS_INLINE void 1.43 +Debug_SetValueRangeToCrashOnTouch(Value *beg, Value *end) 1.44 +{ 1.45 +#ifdef DEBUG 1.46 + for (Value *v = beg; v != end; ++v) 1.47 + v->setObject(*reinterpret_cast<JSObject *>(0x42)); 1.48 +#endif 1.49 +} 1.50 + 1.51 +static MOZ_ALWAYS_INLINE void 1.52 +Debug_SetValueRangeToCrashOnTouch(Value *vec, size_t len) 1.53 +{ 1.54 +#ifdef DEBUG 1.55 + Debug_SetValueRangeToCrashOnTouch(vec, vec + len); 1.56 +#endif 1.57 +} 1.58 + 1.59 +static MOZ_ALWAYS_INLINE void 1.60 +Debug_SetValueRangeToCrashOnTouch(HeapValue *vec, size_t len) 1.61 +{ 1.62 +#ifdef DEBUG 1.63 + Debug_SetValueRangeToCrashOnTouch((Value *) vec, len); 1.64 +#endif 1.65 +} 1.66 + 1.67 +static MOZ_ALWAYS_INLINE void 1.68 +Debug_SetSlotRangeToCrashOnTouch(HeapSlot *vec, uint32_t len) 1.69 +{ 1.70 +#ifdef DEBUG 1.71 + Debug_SetValueRangeToCrashOnTouch((Value *) vec, len); 1.72 +#endif 1.73 +} 1.74 + 1.75 +static MOZ_ALWAYS_INLINE void 1.76 +Debug_SetSlotRangeToCrashOnTouch(HeapSlot *begin, HeapSlot *end) 1.77 +{ 1.78 +#ifdef DEBUG 1.79 + Debug_SetValueRangeToCrashOnTouch((Value *) begin, end - begin); 1.80 +#endif 1.81 +} 1.82 + 1.83 +class ArrayObject; 1.84 + 1.85 +/* 1.86 + * ES6 20130308 draft 8.4.2.4 ArraySetLength. 1.87 + * 1.88 + * |id| must be "length", |attrs| are the attributes to be used for the newly- 1.89 + * changed length property, |value| is the value for the new length, and 1.90 + * |setterIsStrict| indicates whether invalid changes will cause a TypeError 1.91 + * to be thrown. 1.92 + */ 1.93 +template <ExecutionMode mode> 1.94 +extern bool 1.95 +ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx, 1.96 + Handle<ArrayObject*> obj, HandleId id, 1.97 + unsigned attrs, HandleValue value, bool setterIsStrict); 1.98 + 1.99 +/* 1.100 + * Elements header used for all objects. The elements component of such objects 1.101 + * offers an efficient representation for all or some of the indexed properties 1.102 + * of the object, using a flat array of Values rather than a shape hierarchy 1.103 + * stored in the object's slots. This structure is immediately followed by an 1.104 + * array of elements, with the elements member in an object pointing to the 1.105 + * beginning of that array (the end of this structure). 1.106 + * See below for usage of this structure. 1.107 + * 1.108 + * The sets of properties represented by an object's elements and slots 1.109 + * are disjoint. The elements contain only indexed properties, while the slots 1.110 + * can contain both named and indexed properties; any indexes in the slots are 1.111 + * distinct from those in the elements. If isIndexed() is false for an object, 1.112 + * all indexed properties (if any) are stored in the dense elements. 1.113 + * 1.114 + * Indexes will be stored in the object's slots instead of its elements in 1.115 + * the following case: 1.116 + * - there are more than MIN_SPARSE_INDEX slots total and the load factor 1.117 + * (COUNT / capacity) is less than 0.25 1.118 + * - a property is defined that has non-default property attributes. 1.119 + * 1.120 + * We track these pieces of metadata for dense elements: 1.121 + * - The length property as a uint32_t, accessible for array objects with 1.122 + * ArrayObject::{length,setLength}(). This is unused for non-arrays. 1.123 + * - The number of element slots (capacity), gettable with 1.124 + * getDenseElementsCapacity(). 1.125 + * - The array's initialized length, accessible with 1.126 + * getDenseElementsInitializedLength(). 1.127 + * 1.128 + * Holes in the array are represented by MagicValue(JS_ELEMENTS_HOLE) values. 1.129 + * These indicate indexes which are not dense properties of the array. The 1.130 + * property may, however, be held by the object's properties. 1.131 + * 1.132 + * The capacity and length of an object's elements are almost entirely 1.133 + * unrelated! In general the length may be greater than, less than, or equal 1.134 + * to the capacity. The first case occurs with |new Array(100)|. The length 1.135 + * is 100, but the capacity remains 0 (indices below length and above capacity 1.136 + * must be treated as holes) until elements between capacity and length are 1.137 + * set. The other two cases are common, depending upon the number of elements 1.138 + * in an array and the underlying allocator used for element storage. 1.139 + * 1.140 + * The only case in which the capacity and length of an object's elements are 1.141 + * related is when the object is an array with non-writable length. In this 1.142 + * case the capacity is always less than or equal to the length. This permits 1.143 + * JIT code to optimize away the check for non-writable length when assigning 1.144 + * to possibly out-of-range elements: such code already has to check for 1.145 + * |index < capacity|, and fallback code checks for non-writable length. 1.146 + * 1.147 + * The initialized length of an object specifies the number of elements that 1.148 + * have been initialized. All elements above the initialized length are 1.149 + * holes in the object, and the memory for all elements between the initialized 1.150 + * length and capacity is left uninitialized. The initialized length is some 1.151 + * value less than or equal to both the object's length and the object's 1.152 + * capacity. 1.153 + * 1.154 + * There is flexibility in exactly the value the initialized length must hold, 1.155 + * e.g. if an array has length 5, capacity 10, completely empty, it is valid 1.156 + * for the initialized length to be any value between zero and 5, as long as 1.157 + * the in memory values below the initialized length have been initialized with 1.158 + * a hole value. However, in such cases we want to keep the initialized length 1.159 + * as small as possible: if the object is known to have no hole values below 1.160 + * its initialized length, then it is "packed" and can be accessed much faster 1.161 + * by JIT code. 1.162 + * 1.163 + * Elements do not track property creation order, so enumerating the elements 1.164 + * of an object does not necessarily visit indexes in the order they were 1.165 + * created. 1.166 + */ 1.167 +class ObjectElements 1.168 +{ 1.169 + public: 1.170 + enum Flags { 1.171 + CONVERT_DOUBLE_ELEMENTS = 0x1, 1.172 + 1.173 + // Present only if these elements correspond to an array with 1.174 + // non-writable length; never present for non-arrays. 1.175 + NONWRITABLE_ARRAY_LENGTH = 0x2 1.176 + }; 1.177 + 1.178 + private: 1.179 + friend class ::JSObject; 1.180 + friend class ObjectImpl; 1.181 + friend class ArrayObject; 1.182 + friend class Nursery; 1.183 + 1.184 + template <ExecutionMode mode> 1.185 + friend bool 1.186 + ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx, 1.187 + Handle<ArrayObject*> obj, HandleId id, 1.188 + unsigned attrs, HandleValue value, bool setterIsStrict); 1.189 + 1.190 + /* See Flags enum above. */ 1.191 + uint32_t flags; 1.192 + 1.193 + /* 1.194 + * Number of initialized elements. This is <= the capacity, and for arrays 1.195 + * is <= the length. Memory for elements above the initialized length is 1.196 + * uninitialized, but values between the initialized length and the proper 1.197 + * length are conceptually holes. 1.198 + */ 1.199 + uint32_t initializedLength; 1.200 + 1.201 + /* Number of allocated slots. */ 1.202 + uint32_t capacity; 1.203 + 1.204 + /* 'length' property of array objects, unused for other objects. */ 1.205 + uint32_t length; 1.206 + 1.207 + void staticAsserts() { 1.208 + static_assert(sizeof(ObjectElements) == VALUES_PER_HEADER * sizeof(Value), 1.209 + "Elements size and values-per-Elements mismatch"); 1.210 + } 1.211 + 1.212 + bool shouldConvertDoubleElements() const { 1.213 + return flags & CONVERT_DOUBLE_ELEMENTS; 1.214 + } 1.215 + void setShouldConvertDoubleElements() { 1.216 + flags |= CONVERT_DOUBLE_ELEMENTS; 1.217 + } 1.218 + void clearShouldConvertDoubleElements() { 1.219 + flags &= ~CONVERT_DOUBLE_ELEMENTS; 1.220 + } 1.221 + bool hasNonwritableArrayLength() const { 1.222 + return flags & NONWRITABLE_ARRAY_LENGTH; 1.223 + } 1.224 + void setNonwritableArrayLength() { 1.225 + flags |= NONWRITABLE_ARRAY_LENGTH; 1.226 + } 1.227 + 1.228 + public: 1.229 + MOZ_CONSTEXPR ObjectElements(uint32_t capacity, uint32_t length) 1.230 + : flags(0), initializedLength(0), capacity(capacity), length(length) 1.231 + {} 1.232 + 1.233 + HeapSlot *elements() { 1.234 + return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements)); 1.235 + } 1.236 + static ObjectElements * fromElements(HeapSlot *elems) { 1.237 + return reinterpret_cast<ObjectElements*>(uintptr_t(elems) - sizeof(ObjectElements)); 1.238 + } 1.239 + 1.240 + static int offsetOfFlags() { 1.241 + return int(offsetof(ObjectElements, flags)) - int(sizeof(ObjectElements)); 1.242 + } 1.243 + static int offsetOfInitializedLength() { 1.244 + return int(offsetof(ObjectElements, initializedLength)) - int(sizeof(ObjectElements)); 1.245 + } 1.246 + static int offsetOfCapacity() { 1.247 + return int(offsetof(ObjectElements, capacity)) - int(sizeof(ObjectElements)); 1.248 + } 1.249 + static int offsetOfLength() { 1.250 + return int(offsetof(ObjectElements, length)) - int(sizeof(ObjectElements)); 1.251 + } 1.252 + 1.253 + static bool ConvertElementsToDoubles(JSContext *cx, uintptr_t elements); 1.254 + 1.255 + static const size_t VALUES_PER_HEADER = 2; 1.256 +}; 1.257 + 1.258 +/* Shared singleton for objects with no elements. */ 1.259 +extern HeapSlot *const emptyObjectElements; 1.260 + 1.261 +struct Class; 1.262 +struct GCMarker; 1.263 +struct ObjectOps; 1.264 +class Shape; 1.265 + 1.266 +class NewObjectCache; 1.267 +class TaggedProto; 1.268 + 1.269 +inline Value 1.270 +ObjectValue(ObjectImpl &obj); 1.271 + 1.272 +#ifdef DEBUG 1.273 +static inline bool 1.274 +IsObjectValueInCompartment(js::Value v, JSCompartment *comp); 1.275 +#endif 1.276 + 1.277 +/* 1.278 + * ObjectImpl specifies the internal implementation of an object. (In contrast 1.279 + * JSObject specifies an "external" interface, at the conceptual level of that 1.280 + * exposed in ECMAScript.) 1.281 + * 1.282 + * The |shape_| member stores the shape of the object, which includes the 1.283 + * object's class and the layout of all its properties. 1.284 + * 1.285 + * The |type_| member stores the type of the object, which contains its 1.286 + * prototype object and the possible types of its properties. 1.287 + * 1.288 + * The rest of the object stores its named properties and indexed elements. 1.289 + * These are stored separately from one another. Objects are followed by a 1.290 + * variable-sized array of values for inline storage, which may be used by 1.291 + * either properties of native objects (fixed slots), by elements (fixed 1.292 + * elements), or by other data for certain kinds of objects, such as 1.293 + * ArrayBufferObjects and TypedArrayObjects. 1.294 + * 1.295 + * Two native objects with the same shape are guaranteed to have the same 1.296 + * number of fixed slots. 1.297 + * 1.298 + * Named property storage can be split between fixed slots and a dynamically 1.299 + * allocated array (the slots member). For an object with N fixed slots, shapes 1.300 + * with slots [0..N-1] are stored in the fixed slots, and the remainder are 1.301 + * stored in the dynamic array. If all properties fit in the fixed slots, the 1.302 + * 'slots' member is nullptr. 1.303 + * 1.304 + * Elements are indexed via the 'elements' member. This member can point to 1.305 + * either the shared emptyObjectElements singleton, into the inline value array 1.306 + * (the address of the third value, to leave room for a ObjectElements header; 1.307 + * in this case numFixedSlots() is zero) or to a dynamically allocated array. 1.308 + * 1.309 + * Only certain combinations of slots and elements storage are possible. 1.310 + * 1.311 + * - For native objects, slots and elements may both be non-empty. The 1.312 + * slots may be either names or indexes; no indexed property will be in both 1.313 + * the slots and elements. 1.314 + * 1.315 + * - For non-native objects, slots and elements are both empty. 1.316 + * 1.317 + * The members of this class are currently protected; in the long run this will 1.318 + * will change so that some members are private, and only certain methods that 1.319 + * act upon them will be protected. 1.320 + */ 1.321 +class ObjectImpl : public gc::BarrieredCell<ObjectImpl> 1.322 +{ 1.323 + friend Zone *js::gc::BarrieredCell<ObjectImpl>::zone() const; 1.324 + friend Zone *js::gc::BarrieredCell<ObjectImpl>::zoneFromAnyThread() const; 1.325 + 1.326 + protected: 1.327 + /* 1.328 + * Shape of the object, encodes the layout of the object's properties and 1.329 + * all other information about its structure. See vm/Shape.h. 1.330 + */ 1.331 + HeapPtrShape shape_; 1.332 + 1.333 + /* 1.334 + * The object's type and prototype. For objects with the LAZY_TYPE flag 1.335 + * set, this is the prototype's default 'new' type and can only be used 1.336 + * to get that prototype. 1.337 + */ 1.338 + HeapPtrTypeObject type_; 1.339 + 1.340 + HeapSlot *slots; /* Slots for object properties. */ 1.341 + HeapSlot *elements; /* Slots for object elements. */ 1.342 + 1.343 + friend bool 1.344 + ArraySetLength(JSContext *cx, Handle<ArrayObject*> obj, HandleId id, unsigned attrs, 1.345 + HandleValue value, bool setterIsStrict); 1.346 + 1.347 + private: 1.348 + static void staticAsserts() { 1.349 + static_assert(sizeof(ObjectImpl) == sizeof(shadow::Object), 1.350 + "shadow interface must match actual implementation"); 1.351 + static_assert(sizeof(ObjectImpl) % sizeof(Value) == 0, 1.352 + "fixed slots after an object must be aligned"); 1.353 + 1.354 + static_assert(offsetof(ObjectImpl, shape_) == offsetof(shadow::Object, shape), 1.355 + "shadow shape must match actual shape"); 1.356 + static_assert(offsetof(ObjectImpl, type_) == offsetof(shadow::Object, type), 1.357 + "shadow type must match actual type"); 1.358 + static_assert(offsetof(ObjectImpl, slots) == offsetof(shadow::Object, slots), 1.359 + "shadow slots must match actual slots"); 1.360 + static_assert(offsetof(ObjectImpl, elements) == offsetof(shadow::Object, _1), 1.361 + "shadow placeholder must match actual elements"); 1.362 + } 1.363 + 1.364 + JSObject * asObjectPtr() { return reinterpret_cast<JSObject *>(this); } 1.365 + const JSObject * asObjectPtr() const { return reinterpret_cast<const JSObject *>(this); } 1.366 + 1.367 + friend inline Value ObjectValue(ObjectImpl &obj); 1.368 + 1.369 + /* These functions are public, and they should remain public. */ 1.370 + 1.371 + public: 1.372 + TaggedProto getTaggedProto() const { 1.373 + return type_->proto(); 1.374 + } 1.375 + 1.376 + bool hasTenuredProto() const; 1.377 + 1.378 + const Class *getClass() const { 1.379 + return type_->clasp(); 1.380 + } 1.381 + 1.382 + static inline bool 1.383 + isExtensible(ExclusiveContext *cx, Handle<ObjectImpl*> obj, bool *extensible); 1.384 + 1.385 + // Indicates whether a non-proxy is extensible. Don't call on proxies! 1.386 + // This method really shouldn't exist -- but there are a few internal 1.387 + // places that want it (JITs and the like), and it'd be a pain to mark them 1.388 + // all as friends. 1.389 + bool nonProxyIsExtensible() const { 1.390 + MOZ_ASSERT(!isProxy()); 1.391 + 1.392 + // [[Extensible]] for ordinary non-proxy objects is an object flag. 1.393 + return !lastProperty()->hasObjectFlag(BaseShape::NOT_EXTENSIBLE); 1.394 + } 1.395 + 1.396 +#ifdef DEBUG 1.397 + bool isProxy() const; 1.398 +#endif 1.399 + 1.400 + // Attempt to change the [[Extensible]] bit on |obj| to false. Callers 1.401 + // must ensure that |obj| is currently extensible before calling this! 1.402 + static bool 1.403 + preventExtensions(JSContext *cx, Handle<ObjectImpl*> obj); 1.404 + 1.405 + HeapSlotArray getDenseElements() { 1.406 + JS_ASSERT(isNative()); 1.407 + return HeapSlotArray(elements); 1.408 + } 1.409 + const Value &getDenseElement(uint32_t idx) { 1.410 + JS_ASSERT(isNative()); 1.411 + MOZ_ASSERT(idx < getDenseInitializedLength()); 1.412 + return elements[idx]; 1.413 + } 1.414 + bool containsDenseElement(uint32_t idx) { 1.415 + JS_ASSERT(isNative()); 1.416 + return idx < getDenseInitializedLength() && !elements[idx].isMagic(JS_ELEMENTS_HOLE); 1.417 + } 1.418 + uint32_t getDenseInitializedLength() { 1.419 + JS_ASSERT(getClass()->isNative()); 1.420 + return getElementsHeader()->initializedLength; 1.421 + } 1.422 + uint32_t getDenseCapacity() { 1.423 + JS_ASSERT(getClass()->isNative()); 1.424 + return getElementsHeader()->capacity; 1.425 + } 1.426 + 1.427 + protected: 1.428 +#ifdef DEBUG 1.429 + void checkShapeConsistency(); 1.430 +#else 1.431 + void checkShapeConsistency() { } 1.432 +#endif 1.433 + 1.434 + Shape * 1.435 + replaceWithNewEquivalentShape(ThreadSafeContext *cx, 1.436 + Shape *existingShape, Shape *newShape = nullptr); 1.437 + 1.438 + enum GenerateShape { 1.439 + GENERATE_NONE, 1.440 + GENERATE_SHAPE 1.441 + }; 1.442 + 1.443 + bool setFlag(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag, 1.444 + GenerateShape generateShape = GENERATE_NONE); 1.445 + bool clearFlag(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag); 1.446 + 1.447 + bool toDictionaryMode(ThreadSafeContext *cx); 1.448 + 1.449 + private: 1.450 + friend class Nursery; 1.451 + 1.452 + /* 1.453 + * Get internal pointers to the range of values starting at start and 1.454 + * running for length. 1.455 + */ 1.456 + void getSlotRangeUnchecked(uint32_t start, uint32_t length, 1.457 + HeapSlot **fixedStart, HeapSlot **fixedEnd, 1.458 + HeapSlot **slotsStart, HeapSlot **slotsEnd) 1.459 + { 1.460 + MOZ_ASSERT(start + length >= start); 1.461 + 1.462 + uint32_t fixed = numFixedSlots(); 1.463 + if (start < fixed) { 1.464 + if (start + length < fixed) { 1.465 + *fixedStart = &fixedSlots()[start]; 1.466 + *fixedEnd = &fixedSlots()[start + length]; 1.467 + *slotsStart = *slotsEnd = nullptr; 1.468 + } else { 1.469 + uint32_t localCopy = fixed - start; 1.470 + *fixedStart = &fixedSlots()[start]; 1.471 + *fixedEnd = &fixedSlots()[start + localCopy]; 1.472 + *slotsStart = &slots[0]; 1.473 + *slotsEnd = &slots[length - localCopy]; 1.474 + } 1.475 + } else { 1.476 + *fixedStart = *fixedEnd = nullptr; 1.477 + *slotsStart = &slots[start - fixed]; 1.478 + *slotsEnd = &slots[start - fixed + length]; 1.479 + } 1.480 + } 1.481 + 1.482 + void getSlotRange(uint32_t start, uint32_t length, 1.483 + HeapSlot **fixedStart, HeapSlot **fixedEnd, 1.484 + HeapSlot **slotsStart, HeapSlot **slotsEnd) 1.485 + { 1.486 + MOZ_ASSERT(slotInRange(start + length, SENTINEL_ALLOWED)); 1.487 + getSlotRangeUnchecked(start, length, fixedStart, fixedEnd, slotsStart, slotsEnd); 1.488 + } 1.489 + 1.490 + protected: 1.491 + friend struct GCMarker; 1.492 + friend class Shape; 1.493 + friend class NewObjectCache; 1.494 + 1.495 + void invalidateSlotRange(uint32_t start, uint32_t length) { 1.496 +#ifdef DEBUG 1.497 + HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd; 1.498 + getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd); 1.499 + Debug_SetSlotRangeToCrashOnTouch(fixedStart, fixedEnd); 1.500 + Debug_SetSlotRangeToCrashOnTouch(slotsStart, slotsEnd); 1.501 +#endif /* DEBUG */ 1.502 + } 1.503 + 1.504 + void initializeSlotRange(uint32_t start, uint32_t count); 1.505 + 1.506 + /* 1.507 + * Initialize a flat array of slots to this object at a start slot. The 1.508 + * caller must ensure that are enough slots. 1.509 + */ 1.510 + void initSlotRange(uint32_t start, const Value *vector, uint32_t length); 1.511 + 1.512 + /* 1.513 + * Copy a flat array of slots to this object at a start slot. Caller must 1.514 + * ensure there are enough slots in this object. 1.515 + */ 1.516 + void copySlotRange(uint32_t start, const Value *vector, uint32_t length); 1.517 + 1.518 +#ifdef DEBUG 1.519 + enum SentinelAllowed { 1.520 + SENTINEL_NOT_ALLOWED, 1.521 + SENTINEL_ALLOWED 1.522 + }; 1.523 + 1.524 + /* 1.525 + * Check that slot is in range for the object's allocated slots. 1.526 + * If sentinelAllowed then slot may equal the slot capacity. 1.527 + */ 1.528 + bool slotInRange(uint32_t slot, SentinelAllowed sentinel = SENTINEL_NOT_ALLOWED) const; 1.529 +#endif 1.530 + 1.531 + /* 1.532 + * Minimum size for dynamically allocated slots in normal Objects. 1.533 + * ArrayObjects don't use this limit and can have a lower slot capacity, 1.534 + * since they normally don't have a lot of slots. 1.535 + */ 1.536 + static const uint32_t SLOT_CAPACITY_MIN = 8; 1.537 + 1.538 + HeapSlot *fixedSlots() const { 1.539 + return reinterpret_cast<HeapSlot *>(uintptr_t(this) + sizeof(ObjectImpl)); 1.540 + } 1.541 + 1.542 + /* 1.543 + * These functions are currently public for simplicity; in the long run 1.544 + * it may make sense to make at least some of them private. 1.545 + */ 1.546 + 1.547 + public: 1.548 + Shape * lastProperty() const { 1.549 + MOZ_ASSERT(shape_); 1.550 + return shape_; 1.551 + } 1.552 + 1.553 + bool generateOwnShape(ThreadSafeContext *cx, js::Shape *newShape = nullptr) { 1.554 + return replaceWithNewEquivalentShape(cx, lastProperty(), newShape); 1.555 + } 1.556 + 1.557 + JSCompartment *compartment() const { 1.558 + return lastProperty()->base()->compartment(); 1.559 + } 1.560 + 1.561 + bool isNative() const { 1.562 + return lastProperty()->isNative(); 1.563 + } 1.564 + 1.565 + types::TypeObject *type() const { 1.566 + MOZ_ASSERT(!hasLazyType()); 1.567 + return typeRaw(); 1.568 + } 1.569 + 1.570 + types::TypeObject *typeRaw() const { 1.571 + return type_; 1.572 + } 1.573 + 1.574 + uint32_t numFixedSlots() const { 1.575 + return reinterpret_cast<const shadow::Object *>(this)->numFixedSlots(); 1.576 + } 1.577 + 1.578 + /* 1.579 + * Whether this is the only object which has its specified type. This 1.580 + * object will have its type constructed lazily as needed by analysis. 1.581 + */ 1.582 + bool hasSingletonType() const { 1.583 + return !!type_->singleton(); 1.584 + } 1.585 + 1.586 + /* 1.587 + * Whether the object's type has not been constructed yet. If an object 1.588 + * might have a lazy type, use getType() below, otherwise type(). 1.589 + */ 1.590 + bool hasLazyType() const { 1.591 + return type_->lazy(); 1.592 + } 1.593 + 1.594 + uint32_t slotSpan() const { 1.595 + if (inDictionaryMode()) 1.596 + return lastProperty()->base()->slotSpan(); 1.597 + return lastProperty()->slotSpan(); 1.598 + } 1.599 + 1.600 + /* Compute dynamicSlotsCount() for this object. */ 1.601 + uint32_t numDynamicSlots() const { 1.602 + return dynamicSlotsCount(numFixedSlots(), slotSpan(), getClass()); 1.603 + } 1.604 + 1.605 + 1.606 + Shape *nativeLookup(ExclusiveContext *cx, jsid id); 1.607 + Shape *nativeLookup(ExclusiveContext *cx, PropertyName *name) { 1.608 + return nativeLookup(cx, NameToId(name)); 1.609 + } 1.610 + 1.611 + bool nativeContains(ExclusiveContext *cx, jsid id) { 1.612 + return nativeLookup(cx, id) != nullptr; 1.613 + } 1.614 + bool nativeContains(ExclusiveContext *cx, PropertyName* name) { 1.615 + return nativeLookup(cx, name) != nullptr; 1.616 + } 1.617 + bool nativeContains(ExclusiveContext *cx, Shape* shape) { 1.618 + return nativeLookup(cx, shape->propid()) == shape; 1.619 + } 1.620 + 1.621 + /* Contextless; can be called from parallel code. */ 1.622 + Shape *nativeLookupPure(jsid id); 1.623 + Shape *nativeLookupPure(PropertyName *name) { 1.624 + return nativeLookupPure(NameToId(name)); 1.625 + } 1.626 + 1.627 + bool nativeContainsPure(jsid id) { 1.628 + return nativeLookupPure(id) != nullptr; 1.629 + } 1.630 + bool nativeContainsPure(PropertyName* name) { 1.631 + return nativeContainsPure(NameToId(name)); 1.632 + } 1.633 + bool nativeContainsPure(Shape* shape) { 1.634 + return nativeLookupPure(shape->propid()) == shape; 1.635 + } 1.636 + 1.637 + const JSClass *getJSClass() const { 1.638 + return Jsvalify(getClass()); 1.639 + } 1.640 + bool hasClass(const Class *c) const { 1.641 + return getClass() == c; 1.642 + } 1.643 + const ObjectOps *getOps() const { 1.644 + return &getClass()->ops; 1.645 + } 1.646 + 1.647 + /* 1.648 + * An object is a delegate if it is on another object's prototype or scope 1.649 + * chain, and therefore the delegate might be asked implicitly to get or 1.650 + * set a property on behalf of another object. Delegates may be accessed 1.651 + * directly too, as may any object, but only those objects linked after the 1.652 + * head of any prototype or scope chain are flagged as delegates. This 1.653 + * definition helps to optimize shape-based property cache invalidation 1.654 + * (see Purge{Scope,Proto}Chain in jsobj.cpp). 1.655 + */ 1.656 + bool isDelegate() const { 1.657 + return lastProperty()->hasObjectFlag(BaseShape::DELEGATE); 1.658 + } 1.659 + 1.660 + /* 1.661 + * Return true if this object is a native one that has been converted from 1.662 + * shared-immutable prototype-rooted shape storage to dictionary-shapes in 1.663 + * a doubly-linked list. 1.664 + */ 1.665 + bool inDictionaryMode() const { 1.666 + return lastProperty()->inDictionary(); 1.667 + } 1.668 + 1.669 + const Value &getSlot(uint32_t slot) const { 1.670 + MOZ_ASSERT(slotInRange(slot)); 1.671 + uint32_t fixed = numFixedSlots(); 1.672 + if (slot < fixed) 1.673 + return fixedSlots()[slot]; 1.674 + return slots[slot - fixed]; 1.675 + } 1.676 + 1.677 + const HeapSlot *getSlotAddressUnchecked(uint32_t slot) const { 1.678 + uint32_t fixed = numFixedSlots(); 1.679 + if (slot < fixed) 1.680 + return fixedSlots() + slot; 1.681 + return slots + (slot - fixed); 1.682 + } 1.683 + 1.684 + HeapSlot *getSlotAddressUnchecked(uint32_t slot) { 1.685 + const ObjectImpl *obj = static_cast<const ObjectImpl*>(this); 1.686 + return const_cast<HeapSlot*>(obj->getSlotAddressUnchecked(slot)); 1.687 + } 1.688 + 1.689 + HeapSlot *getSlotAddress(uint32_t slot) { 1.690 + /* 1.691 + * This can be used to get the address of the end of the slots for the 1.692 + * object, which may be necessary when fetching zero-length arrays of 1.693 + * slots (e.g. for callObjVarArray). 1.694 + */ 1.695 + MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED)); 1.696 + return getSlotAddressUnchecked(slot); 1.697 + } 1.698 + 1.699 + const HeapSlot *getSlotAddress(uint32_t slot) const { 1.700 + /* 1.701 + * This can be used to get the address of the end of the slots for the 1.702 + * object, which may be necessary when fetching zero-length arrays of 1.703 + * slots (e.g. for callObjVarArray). 1.704 + */ 1.705 + MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED)); 1.706 + return getSlotAddressUnchecked(slot); 1.707 + } 1.708 + 1.709 + HeapSlot &getSlotRef(uint32_t slot) { 1.710 + MOZ_ASSERT(slotInRange(slot)); 1.711 + return *getSlotAddress(slot); 1.712 + } 1.713 + 1.714 + const HeapSlot &getSlotRef(uint32_t slot) const { 1.715 + MOZ_ASSERT(slotInRange(slot)); 1.716 + return *getSlotAddress(slot); 1.717 + } 1.718 + 1.719 + HeapSlot &nativeGetSlotRef(uint32_t slot) { 1.720 + JS_ASSERT(isNative() && slot < slotSpan()); 1.721 + return getSlotRef(slot); 1.722 + } 1.723 + const Value &nativeGetSlot(uint32_t slot) const { 1.724 + JS_ASSERT(isNative() && slot < slotSpan()); 1.725 + return getSlot(slot); 1.726 + } 1.727 + 1.728 + void setSlot(uint32_t slot, const Value &value) { 1.729 + MOZ_ASSERT(slotInRange(slot)); 1.730 + MOZ_ASSERT(IsObjectValueInCompartment(value, compartment())); 1.731 + getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value); 1.732 + } 1.733 + 1.734 + inline void setCrossCompartmentSlot(uint32_t slot, const Value &value) { 1.735 + MOZ_ASSERT(slotInRange(slot)); 1.736 + getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value); 1.737 + } 1.738 + 1.739 + void initSlot(uint32_t slot, const Value &value) { 1.740 + MOZ_ASSERT(getSlot(slot).isUndefined()); 1.741 + MOZ_ASSERT(slotInRange(slot)); 1.742 + MOZ_ASSERT(IsObjectValueInCompartment(value, compartment())); 1.743 + initSlotUnchecked(slot, value); 1.744 + } 1.745 + 1.746 + void initCrossCompartmentSlot(uint32_t slot, const Value &value) { 1.747 + MOZ_ASSERT(getSlot(slot).isUndefined()); 1.748 + MOZ_ASSERT(slotInRange(slot)); 1.749 + initSlotUnchecked(slot, value); 1.750 + } 1.751 + 1.752 + void initSlotUnchecked(uint32_t slot, const Value &value) { 1.753 + getSlotAddressUnchecked(slot)->init(this->asObjectPtr(), HeapSlot::Slot, slot, value); 1.754 + } 1.755 + 1.756 + /* For slots which are known to always be fixed, due to the way they are allocated. */ 1.757 + 1.758 + HeapSlot &getFixedSlotRef(uint32_t slot) { 1.759 + MOZ_ASSERT(slot < numFixedSlots()); 1.760 + return fixedSlots()[slot]; 1.761 + } 1.762 + 1.763 + const Value &getFixedSlot(uint32_t slot) const { 1.764 + MOZ_ASSERT(slot < numFixedSlots()); 1.765 + return fixedSlots()[slot]; 1.766 + } 1.767 + 1.768 + void setFixedSlot(uint32_t slot, const Value &value) { 1.769 + MOZ_ASSERT(slot < numFixedSlots()); 1.770 + fixedSlots()[slot].set(this->asObjectPtr(), HeapSlot::Slot, slot, value); 1.771 + } 1.772 + 1.773 + void initFixedSlot(uint32_t slot, const Value &value) { 1.774 + MOZ_ASSERT(slot < numFixedSlots()); 1.775 + fixedSlots()[slot].init(this->asObjectPtr(), HeapSlot::Slot, slot, value); 1.776 + } 1.777 + 1.778 + /* 1.779 + * Get the number of dynamic slots to allocate to cover the properties in 1.780 + * an object with the given number of fixed slots and slot span. The slot 1.781 + * capacity is not stored explicitly, and the allocated size of the slot 1.782 + * array is kept in sync with this count. 1.783 + */ 1.784 + static uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class *clasp); 1.785 + 1.786 + /* Memory usage functions. */ 1.787 + size_t tenuredSizeOfThis() const { 1.788 + return js::gc::Arena::thingSize(tenuredGetAllocKind()); 1.789 + } 1.790 + 1.791 + /* Elements accessors. */ 1.792 + 1.793 + ObjectElements * getElementsHeader() const { 1.794 + return ObjectElements::fromElements(elements); 1.795 + } 1.796 + 1.797 + inline HeapSlot *fixedElements() const { 1.798 + static_assert(2 * sizeof(Value) == sizeof(ObjectElements), 1.799 + "when elements are stored inline, the first two " 1.800 + "slots will hold the ObjectElements header"); 1.801 + return &fixedSlots()[2]; 1.802 + } 1.803 + 1.804 +#ifdef DEBUG 1.805 + bool canHaveNonEmptyElements(); 1.806 +#endif 1.807 + 1.808 + void setFixedElements() { 1.809 + JS_ASSERT(canHaveNonEmptyElements()); 1.810 + this->elements = fixedElements(); 1.811 + } 1.812 + 1.813 + inline bool hasDynamicElements() const { 1.814 + /* 1.815 + * Note: for objects with zero fixed slots this could potentially give 1.816 + * a spurious 'true' result, if the end of this object is exactly 1.817 + * aligned with the end of its arena and dynamic slots are allocated 1.818 + * immediately afterwards. Such cases cannot occur for dense arrays 1.819 + * (which have at least two fixed slots) and can only result in a leak. 1.820 + */ 1.821 + return !hasEmptyElements() && elements != fixedElements(); 1.822 + } 1.823 + 1.824 + inline bool hasFixedElements() const { 1.825 + return elements == fixedElements(); 1.826 + } 1.827 + 1.828 + inline bool hasEmptyElements() const { 1.829 + return elements == emptyObjectElements; 1.830 + } 1.831 + 1.832 + /* 1.833 + * Get a pointer to the unused data in the object's allocation immediately 1.834 + * following this object, for use with objects which allocate a larger size 1.835 + * class than they need and store non-elements data inline. 1.836 + */ 1.837 + inline void *fixedData(size_t nslots) const; 1.838 + 1.839 + /* GC support. */ 1.840 + static ThingRootKind rootKind() { return THING_ROOT_OBJECT; } 1.841 + 1.842 + inline void privateWriteBarrierPre(void **oldval); 1.843 + 1.844 + void privateWriteBarrierPost(void **pprivate) { 1.845 +#ifdef JSGC_GENERATIONAL 1.846 + shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell(reinterpret_cast<js::gc::Cell **>(pprivate)); 1.847 +#endif 1.848 + } 1.849 + 1.850 + void markChildren(JSTracer *trc); 1.851 + 1.852 + /* Private data accessors. */ 1.853 + 1.854 + inline void *&privateRef(uint32_t nfixed) const { /* XXX should be private, not protected! */ 1.855 + /* 1.856 + * The private pointer of an object can hold any word sized value. 1.857 + * Private pointers are stored immediately after the last fixed slot of 1.858 + * the object. 1.859 + */ 1.860 + MOZ_ASSERT(nfixed == numFixedSlots()); 1.861 + MOZ_ASSERT(hasPrivate()); 1.862 + HeapSlot *end = &fixedSlots()[nfixed]; 1.863 + return *reinterpret_cast<void**>(end); 1.864 + } 1.865 + 1.866 + bool hasPrivate() const { 1.867 + return getClass()->hasPrivate(); 1.868 + } 1.869 + void *getPrivate() const { 1.870 + return privateRef(numFixedSlots()); 1.871 + } 1.872 + void setPrivate(void *data) { 1.873 + void **pprivate = &privateRef(numFixedSlots()); 1.874 + privateWriteBarrierPre(pprivate); 1.875 + *pprivate = data; 1.876 + } 1.877 + 1.878 + void setPrivateGCThing(gc::Cell *cell) { 1.879 + void **pprivate = &privateRef(numFixedSlots()); 1.880 + privateWriteBarrierPre(pprivate); 1.881 + *pprivate = reinterpret_cast<void *>(cell); 1.882 + privateWriteBarrierPost(pprivate); 1.883 + } 1.884 + 1.885 + void setPrivateUnbarriered(void *data) { 1.886 + void **pprivate = &privateRef(numFixedSlots()); 1.887 + *pprivate = data; 1.888 + } 1.889 + void initPrivate(void *data) { 1.890 + privateRef(numFixedSlots()) = data; 1.891 + } 1.892 + 1.893 + /* Access private data for an object with a known number of fixed slots. */ 1.894 + inline void *getPrivate(uint32_t nfixed) const { 1.895 + return privateRef(nfixed); 1.896 + } 1.897 + 1.898 + /* GC Accessors */ 1.899 + void setInitialSlots(HeapSlot *newSlots) { slots = newSlots; } 1.900 + 1.901 + /* JIT Accessors */ 1.902 + static size_t offsetOfShape() { return offsetof(ObjectImpl, shape_); } 1.903 + HeapPtrShape *addressOfShape() { return &shape_; } 1.904 + 1.905 + static size_t offsetOfType() { return offsetof(ObjectImpl, type_); } 1.906 + HeapPtrTypeObject *addressOfType() { return &type_; } 1.907 + 1.908 + static size_t offsetOfElements() { return offsetof(ObjectImpl, elements); } 1.909 + static size_t offsetOfFixedElements() { 1.910 + return sizeof(ObjectImpl) + sizeof(ObjectElements); 1.911 + } 1.912 + 1.913 + static size_t getFixedSlotOffset(size_t slot) { 1.914 + return sizeof(ObjectImpl) + slot * sizeof(Value); 1.915 + } 1.916 + static size_t getPrivateDataOffset(size_t nfixed) { return getFixedSlotOffset(nfixed); } 1.917 + static size_t offsetOfSlots() { return offsetof(ObjectImpl, slots); } 1.918 +}; 1.919 + 1.920 +namespace gc { 1.921 + 1.922 +template <> 1.923 +MOZ_ALWAYS_INLINE Zone * 1.924 +BarrieredCell<ObjectImpl>::zone() const 1.925 +{ 1.926 + const ObjectImpl* obj = static_cast<const ObjectImpl*>(this); 1.927 + JS::Zone *zone = obj->shape_->zone(); 1.928 + JS_ASSERT(CurrentThreadCanAccessZone(zone)); 1.929 + return zone; 1.930 +} 1.931 + 1.932 +template <> 1.933 +MOZ_ALWAYS_INLINE Zone * 1.934 +BarrieredCell<ObjectImpl>::zoneFromAnyThread() const 1.935 +{ 1.936 + const ObjectImpl* obj = static_cast<const ObjectImpl*>(this); 1.937 + return obj->shape_->zoneFromAnyThread(); 1.938 +} 1.939 + 1.940 +// TypeScript::global uses 0x1 as a special value. 1.941 +template<> 1.942 +/* static */ inline bool 1.943 +BarrieredCell<ObjectImpl>::isNullLike(ObjectImpl *obj) 1.944 +{ 1.945 + return IsNullTaggedPointer(obj); 1.946 +} 1.947 + 1.948 +template<> 1.949 +/* static */ inline void 1.950 +BarrieredCell<ObjectImpl>::writeBarrierPost(ObjectImpl *obj, void *addr) 1.951 +{ 1.952 +#ifdef JSGC_GENERATIONAL 1.953 + if (IsNullTaggedPointer(obj)) 1.954 + return; 1.955 + obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell((Cell **)addr); 1.956 +#endif 1.957 +} 1.958 + 1.959 +template<> 1.960 +/* static */ inline void 1.961 +BarrieredCell<ObjectImpl>::writeBarrierPostRelocate(ObjectImpl *obj, void *addr) 1.962 +{ 1.963 +#ifdef JSGC_GENERATIONAL 1.964 + obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putRelocatableCell((Cell **)addr); 1.965 +#endif 1.966 +} 1.967 + 1.968 +template<> 1.969 +/* static */ inline void 1.970 +BarrieredCell<ObjectImpl>::writeBarrierPostRemove(ObjectImpl *obj, void *addr) 1.971 +{ 1.972 +#ifdef JSGC_GENERATIONAL 1.973 + obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->removeRelocatableCell((Cell **)addr); 1.974 +#endif 1.975 +} 1.976 + 1.977 +} // namespace gc 1.978 + 1.979 +inline void 1.980 +ObjectImpl::privateWriteBarrierPre(void **oldval) 1.981 +{ 1.982 +#ifdef JSGC_INCREMENTAL 1.983 + JS::shadow::Zone *shadowZone = this->shadowZoneFromAnyThread(); 1.984 + if (shadowZone->needsBarrier()) { 1.985 + if (*oldval && getClass()->trace) 1.986 + getClass()->trace(shadowZone->barrierTracer(), this->asObjectPtr()); 1.987 + } 1.988 +#endif 1.989 +} 1.990 + 1.991 +inline Value 1.992 +ObjectValue(ObjectImpl &obj) 1.993 +{ 1.994 + Value v; 1.995 + v.setObject(*obj.asObjectPtr()); 1.996 + return v; 1.997 +} 1.998 + 1.999 +inline Handle<JSObject*> 1.1000 +Downcast(Handle<ObjectImpl*> obj) 1.1001 +{ 1.1002 + return Handle<JSObject*>::fromMarkedLocation(reinterpret_cast<JSObject* const*>(obj.address())); 1.1003 +} 1.1004 + 1.1005 +#ifdef DEBUG 1.1006 +static inline bool 1.1007 +IsObjectValueInCompartment(js::Value v, JSCompartment *comp) 1.1008 +{ 1.1009 + if (!v.isObject()) 1.1010 + return true; 1.1011 + return reinterpret_cast<ObjectImpl*>(&v.toObject())->compartment() == comp; 1.1012 +} 1.1013 +#endif 1.1014 + 1.1015 +} /* namespace js */ 1.1016 + 1.1017 +#endif /* vm_ObjectImpl_h */