Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #ifndef vm_ObjectImpl_h |
michael@0 | 8 | #define vm_ObjectImpl_h |
michael@0 | 9 | |
michael@0 | 10 | #include "mozilla/Assertions.h" |
michael@0 | 11 | #include "mozilla/Attributes.h" |
michael@0 | 12 | |
michael@0 | 13 | #include <stdint.h> |
michael@0 | 14 | |
michael@0 | 15 | #include "jsfriendapi.h" |
michael@0 | 16 | #include "jsinfer.h" |
michael@0 | 17 | #include "NamespaceImports.h" |
michael@0 | 18 | |
michael@0 | 19 | #include "gc/Barrier.h" |
michael@0 | 20 | #include "gc/Heap.h" |
michael@0 | 21 | #include "gc/Marking.h" |
michael@0 | 22 | #include "js/Value.h" |
michael@0 | 23 | #include "vm/NumericConversions.h" |
michael@0 | 24 | #include "vm/Shape.h" |
michael@0 | 25 | #include "vm/String.h" |
michael@0 | 26 | |
michael@0 | 27 | namespace js { |
michael@0 | 28 | |
michael@0 | 29 | class ObjectImpl; |
michael@0 | 30 | class Nursery; |
michael@0 | 31 | class Shape; |
michael@0 | 32 | |
michael@0 | 33 | /* |
michael@0 | 34 | * To really poison a set of values, using 'magic' or 'undefined' isn't good |
michael@0 | 35 | * enough since often these will just be ignored by buggy code (see bug 629974) |
michael@0 | 36 | * in debug builds and crash in release builds. Instead, we use a safe-for-crash |
michael@0 | 37 | * pointer. |
michael@0 | 38 | */ |
michael@0 | 39 | static MOZ_ALWAYS_INLINE void |
michael@0 | 40 | Debug_SetValueRangeToCrashOnTouch(Value *beg, Value *end) |
michael@0 | 41 | { |
michael@0 | 42 | #ifdef DEBUG |
michael@0 | 43 | for (Value *v = beg; v != end; ++v) |
michael@0 | 44 | v->setObject(*reinterpret_cast<JSObject *>(0x42)); |
michael@0 | 45 | #endif |
michael@0 | 46 | } |
michael@0 | 47 | |
michael@0 | 48 | static MOZ_ALWAYS_INLINE void |
michael@0 | 49 | Debug_SetValueRangeToCrashOnTouch(Value *vec, size_t len) |
michael@0 | 50 | { |
michael@0 | 51 | #ifdef DEBUG |
michael@0 | 52 | Debug_SetValueRangeToCrashOnTouch(vec, vec + len); |
michael@0 | 53 | #endif |
michael@0 | 54 | } |
michael@0 | 55 | |
michael@0 | 56 | static MOZ_ALWAYS_INLINE void |
michael@0 | 57 | Debug_SetValueRangeToCrashOnTouch(HeapValue *vec, size_t len) |
michael@0 | 58 | { |
michael@0 | 59 | #ifdef DEBUG |
michael@0 | 60 | Debug_SetValueRangeToCrashOnTouch((Value *) vec, len); |
michael@0 | 61 | #endif |
michael@0 | 62 | } |
michael@0 | 63 | |
michael@0 | 64 | static MOZ_ALWAYS_INLINE void |
michael@0 | 65 | Debug_SetSlotRangeToCrashOnTouch(HeapSlot *vec, uint32_t len) |
michael@0 | 66 | { |
michael@0 | 67 | #ifdef DEBUG |
michael@0 | 68 | Debug_SetValueRangeToCrashOnTouch((Value *) vec, len); |
michael@0 | 69 | #endif |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | static MOZ_ALWAYS_INLINE void |
michael@0 | 73 | Debug_SetSlotRangeToCrashOnTouch(HeapSlot *begin, HeapSlot *end) |
michael@0 | 74 | { |
michael@0 | 75 | #ifdef DEBUG |
michael@0 | 76 | Debug_SetValueRangeToCrashOnTouch((Value *) begin, end - begin); |
michael@0 | 77 | #endif |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | class ArrayObject; |
michael@0 | 81 | |
michael@0 | 82 | /* |
michael@0 | 83 | * ES6 20130308 draft 8.4.2.4 ArraySetLength. |
michael@0 | 84 | * |
michael@0 | 85 | * |id| must be "length", |attrs| are the attributes to be used for the newly- |
michael@0 | 86 | * changed length property, |value| is the value for the new length, and |
michael@0 | 87 | * |setterIsStrict| indicates whether invalid changes will cause a TypeError |
michael@0 | 88 | * to be thrown. |
michael@0 | 89 | */ |
michael@0 | 90 | template <ExecutionMode mode> |
michael@0 | 91 | extern bool |
michael@0 | 92 | ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx, |
michael@0 | 93 | Handle<ArrayObject*> obj, HandleId id, |
michael@0 | 94 | unsigned attrs, HandleValue value, bool setterIsStrict); |
michael@0 | 95 | |
michael@0 | 96 | /* |
michael@0 | 97 | * Elements header used for all objects. The elements component of such objects |
michael@0 | 98 | * offers an efficient representation for all or some of the indexed properties |
michael@0 | 99 | * of the object, using a flat array of Values rather than a shape hierarchy |
michael@0 | 100 | * stored in the object's slots. This structure is immediately followed by an |
michael@0 | 101 | * array of elements, with the elements member in an object pointing to the |
michael@0 | 102 | * beginning of that array (the end of this structure). |
michael@0 | 103 | * See below for usage of this structure. |
michael@0 | 104 | * |
michael@0 | 105 | * The sets of properties represented by an object's elements and slots |
michael@0 | 106 | * are disjoint. The elements contain only indexed properties, while the slots |
michael@0 | 107 | * can contain both named and indexed properties; any indexes in the slots are |
michael@0 | 108 | * distinct from those in the elements. If isIndexed() is false for an object, |
michael@0 | 109 | * all indexed properties (if any) are stored in the dense elements. |
michael@0 | 110 | * |
michael@0 | 111 | * Indexes will be stored in the object's slots instead of its elements in |
michael@0 | 112 | * the following case: |
michael@0 | 113 | * - there are more than MIN_SPARSE_INDEX slots total and the load factor |
michael@0 | 114 | * (COUNT / capacity) is less than 0.25 |
michael@0 | 115 | * - a property is defined that has non-default property attributes. |
michael@0 | 116 | * |
michael@0 | 117 | * We track these pieces of metadata for dense elements: |
michael@0 | 118 | * - The length property as a uint32_t, accessible for array objects with |
michael@0 | 119 | * ArrayObject::{length,setLength}(). This is unused for non-arrays. |
michael@0 | 120 | * - The number of element slots (capacity), gettable with |
michael@0 | 121 | * getDenseElementsCapacity(). |
michael@0 | 122 | * - The array's initialized length, accessible with |
michael@0 | 123 | * getDenseElementsInitializedLength(). |
michael@0 | 124 | * |
michael@0 | 125 | * Holes in the array are represented by MagicValue(JS_ELEMENTS_HOLE) values. |
michael@0 | 126 | * These indicate indexes which are not dense properties of the array. The |
michael@0 | 127 | * property may, however, be held by the object's properties. |
michael@0 | 128 | * |
michael@0 | 129 | * The capacity and length of an object's elements are almost entirely |
michael@0 | 130 | * unrelated! In general the length may be greater than, less than, or equal |
michael@0 | 131 | * to the capacity. The first case occurs with |new Array(100)|. The length |
michael@0 | 132 | * is 100, but the capacity remains 0 (indices below length and above capacity |
michael@0 | 133 | * must be treated as holes) until elements between capacity and length are |
michael@0 | 134 | * set. The other two cases are common, depending upon the number of elements |
michael@0 | 135 | * in an array and the underlying allocator used for element storage. |
michael@0 | 136 | * |
michael@0 | 137 | * The only case in which the capacity and length of an object's elements are |
michael@0 | 138 | * related is when the object is an array with non-writable length. In this |
michael@0 | 139 | * case the capacity is always less than or equal to the length. This permits |
michael@0 | 140 | * JIT code to optimize away the check for non-writable length when assigning |
michael@0 | 141 | * to possibly out-of-range elements: such code already has to check for |
michael@0 | 142 | * |index < capacity|, and fallback code checks for non-writable length. |
michael@0 | 143 | * |
michael@0 | 144 | * The initialized length of an object specifies the number of elements that |
michael@0 | 145 | * have been initialized. All elements above the initialized length are |
michael@0 | 146 | * holes in the object, and the memory for all elements between the initialized |
michael@0 | 147 | * length and capacity is left uninitialized. The initialized length is some |
michael@0 | 148 | * value less than or equal to both the object's length and the object's |
michael@0 | 149 | * capacity. |
michael@0 | 150 | * |
michael@0 | 151 | * There is flexibility in exactly the value the initialized length must hold, |
michael@0 | 152 | * e.g. if an array has length 5, capacity 10, completely empty, it is valid |
michael@0 | 153 | * for the initialized length to be any value between zero and 5, as long as |
michael@0 | 154 | * the in memory values below the initialized length have been initialized with |
michael@0 | 155 | * a hole value. However, in such cases we want to keep the initialized length |
michael@0 | 156 | * as small as possible: if the object is known to have no hole values below |
michael@0 | 157 | * its initialized length, then it is "packed" and can be accessed much faster |
michael@0 | 158 | * by JIT code. |
michael@0 | 159 | * |
michael@0 | 160 | * Elements do not track property creation order, so enumerating the elements |
michael@0 | 161 | * of an object does not necessarily visit indexes in the order they were |
michael@0 | 162 | * created. |
michael@0 | 163 | */ |
michael@0 | 164 | class ObjectElements |
michael@0 | 165 | { |
michael@0 | 166 | public: |
michael@0 | 167 | enum Flags { |
michael@0 | 168 | CONVERT_DOUBLE_ELEMENTS = 0x1, |
michael@0 | 169 | |
michael@0 | 170 | // Present only if these elements correspond to an array with |
michael@0 | 171 | // non-writable length; never present for non-arrays. |
michael@0 | 172 | NONWRITABLE_ARRAY_LENGTH = 0x2 |
michael@0 | 173 | }; |
michael@0 | 174 | |
michael@0 | 175 | private: |
michael@0 | 176 | friend class ::JSObject; |
michael@0 | 177 | friend class ObjectImpl; |
michael@0 | 178 | friend class ArrayObject; |
michael@0 | 179 | friend class Nursery; |
michael@0 | 180 | |
michael@0 | 181 | template <ExecutionMode mode> |
michael@0 | 182 | friend bool |
michael@0 | 183 | ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx, |
michael@0 | 184 | Handle<ArrayObject*> obj, HandleId id, |
michael@0 | 185 | unsigned attrs, HandleValue value, bool setterIsStrict); |
michael@0 | 186 | |
michael@0 | 187 | /* See Flags enum above. */ |
michael@0 | 188 | uint32_t flags; |
michael@0 | 189 | |
michael@0 | 190 | /* |
michael@0 | 191 | * Number of initialized elements. This is <= the capacity, and for arrays |
michael@0 | 192 | * is <= the length. Memory for elements above the initialized length is |
michael@0 | 193 | * uninitialized, but values between the initialized length and the proper |
michael@0 | 194 | * length are conceptually holes. |
michael@0 | 195 | */ |
michael@0 | 196 | uint32_t initializedLength; |
michael@0 | 197 | |
michael@0 | 198 | /* Number of allocated slots. */ |
michael@0 | 199 | uint32_t capacity; |
michael@0 | 200 | |
michael@0 | 201 | /* 'length' property of array objects, unused for other objects. */ |
michael@0 | 202 | uint32_t length; |
michael@0 | 203 | |
michael@0 | 204 | void staticAsserts() { |
michael@0 | 205 | static_assert(sizeof(ObjectElements) == VALUES_PER_HEADER * sizeof(Value), |
michael@0 | 206 | "Elements size and values-per-Elements mismatch"); |
michael@0 | 207 | } |
michael@0 | 208 | |
michael@0 | 209 | bool shouldConvertDoubleElements() const { |
michael@0 | 210 | return flags & CONVERT_DOUBLE_ELEMENTS; |
michael@0 | 211 | } |
michael@0 | 212 | void setShouldConvertDoubleElements() { |
michael@0 | 213 | flags |= CONVERT_DOUBLE_ELEMENTS; |
michael@0 | 214 | } |
michael@0 | 215 | void clearShouldConvertDoubleElements() { |
michael@0 | 216 | flags &= ~CONVERT_DOUBLE_ELEMENTS; |
michael@0 | 217 | } |
michael@0 | 218 | bool hasNonwritableArrayLength() const { |
michael@0 | 219 | return flags & NONWRITABLE_ARRAY_LENGTH; |
michael@0 | 220 | } |
michael@0 | 221 | void setNonwritableArrayLength() { |
michael@0 | 222 | flags |= NONWRITABLE_ARRAY_LENGTH; |
michael@0 | 223 | } |
michael@0 | 224 | |
michael@0 | 225 | public: |
michael@0 | 226 | MOZ_CONSTEXPR ObjectElements(uint32_t capacity, uint32_t length) |
michael@0 | 227 | : flags(0), initializedLength(0), capacity(capacity), length(length) |
michael@0 | 228 | {} |
michael@0 | 229 | |
michael@0 | 230 | HeapSlot *elements() { |
michael@0 | 231 | return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements)); |
michael@0 | 232 | } |
michael@0 | 233 | static ObjectElements * fromElements(HeapSlot *elems) { |
michael@0 | 234 | return reinterpret_cast<ObjectElements*>(uintptr_t(elems) - sizeof(ObjectElements)); |
michael@0 | 235 | } |
michael@0 | 236 | |
michael@0 | 237 | static int offsetOfFlags() { |
michael@0 | 238 | return int(offsetof(ObjectElements, flags)) - int(sizeof(ObjectElements)); |
michael@0 | 239 | } |
michael@0 | 240 | static int offsetOfInitializedLength() { |
michael@0 | 241 | return int(offsetof(ObjectElements, initializedLength)) - int(sizeof(ObjectElements)); |
michael@0 | 242 | } |
michael@0 | 243 | static int offsetOfCapacity() { |
michael@0 | 244 | return int(offsetof(ObjectElements, capacity)) - int(sizeof(ObjectElements)); |
michael@0 | 245 | } |
michael@0 | 246 | static int offsetOfLength() { |
michael@0 | 247 | return int(offsetof(ObjectElements, length)) - int(sizeof(ObjectElements)); |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | static bool ConvertElementsToDoubles(JSContext *cx, uintptr_t elements); |
michael@0 | 251 | |
michael@0 | 252 | static const size_t VALUES_PER_HEADER = 2; |
michael@0 | 253 | }; |
michael@0 | 254 | |
michael@0 | 255 | /* Shared singleton for objects with no elements. */ |
michael@0 | 256 | extern HeapSlot *const emptyObjectElements; |
michael@0 | 257 | |
michael@0 | 258 | struct Class; |
michael@0 | 259 | struct GCMarker; |
michael@0 | 260 | struct ObjectOps; |
michael@0 | 261 | class Shape; |
michael@0 | 262 | |
michael@0 | 263 | class NewObjectCache; |
michael@0 | 264 | class TaggedProto; |
michael@0 | 265 | |
michael@0 | 266 | inline Value |
michael@0 | 267 | ObjectValue(ObjectImpl &obj); |
michael@0 | 268 | |
michael@0 | 269 | #ifdef DEBUG |
michael@0 | 270 | static inline bool |
michael@0 | 271 | IsObjectValueInCompartment(js::Value v, JSCompartment *comp); |
michael@0 | 272 | #endif |
michael@0 | 273 | |
michael@0 | 274 | /* |
michael@0 | 275 | * ObjectImpl specifies the internal implementation of an object. (In contrast |
michael@0 | 276 | * JSObject specifies an "external" interface, at the conceptual level of that |
michael@0 | 277 | * exposed in ECMAScript.) |
michael@0 | 278 | * |
michael@0 | 279 | * The |shape_| member stores the shape of the object, which includes the |
michael@0 | 280 | * object's class and the layout of all its properties. |
michael@0 | 281 | * |
michael@0 | 282 | * The |type_| member stores the type of the object, which contains its |
michael@0 | 283 | * prototype object and the possible types of its properties. |
michael@0 | 284 | * |
michael@0 | 285 | * The rest of the object stores its named properties and indexed elements. |
michael@0 | 286 | * These are stored separately from one another. Objects are followed by a |
michael@0 | 287 | * variable-sized array of values for inline storage, which may be used by |
michael@0 | 288 | * either properties of native objects (fixed slots), by elements (fixed |
michael@0 | 289 | * elements), or by other data for certain kinds of objects, such as |
michael@0 | 290 | * ArrayBufferObjects and TypedArrayObjects. |
michael@0 | 291 | * |
michael@0 | 292 | * Two native objects with the same shape are guaranteed to have the same |
michael@0 | 293 | * number of fixed slots. |
michael@0 | 294 | * |
michael@0 | 295 | * Named property storage can be split between fixed slots and a dynamically |
michael@0 | 296 | * allocated array (the slots member). For an object with N fixed slots, shapes |
michael@0 | 297 | * with slots [0..N-1] are stored in the fixed slots, and the remainder are |
michael@0 | 298 | * stored in the dynamic array. If all properties fit in the fixed slots, the |
michael@0 | 299 | * 'slots' member is nullptr. |
michael@0 | 300 | * |
michael@0 | 301 | * Elements are indexed via the 'elements' member. This member can point to |
michael@0 | 302 | * either the shared emptyObjectElements singleton, into the inline value array |
michael@0 | 303 | * (the address of the third value, to leave room for a ObjectElements header; |
michael@0 | 304 | * in this case numFixedSlots() is zero) or to a dynamically allocated array. |
michael@0 | 305 | * |
michael@0 | 306 | * Only certain combinations of slots and elements storage are possible. |
michael@0 | 307 | * |
michael@0 | 308 | * - For native objects, slots and elements may both be non-empty. The |
michael@0 | 309 | * slots may be either names or indexes; no indexed property will be in both |
michael@0 | 310 | * the slots and elements. |
michael@0 | 311 | * |
michael@0 | 312 | * - For non-native objects, slots and elements are both empty. |
michael@0 | 313 | * |
michael@0 | 314 | * The members of this class are currently protected; in the long run this will |
michael@0 | 315 | * will change so that some members are private, and only certain methods that |
michael@0 | 316 | * act upon them will be protected. |
michael@0 | 317 | */ |
michael@0 | 318 | class ObjectImpl : public gc::BarrieredCell<ObjectImpl> |
michael@0 | 319 | { |
michael@0 | 320 | friend Zone *js::gc::BarrieredCell<ObjectImpl>::zone() const; |
michael@0 | 321 | friend Zone *js::gc::BarrieredCell<ObjectImpl>::zoneFromAnyThread() const; |
michael@0 | 322 | |
michael@0 | 323 | protected: |
michael@0 | 324 | /* |
michael@0 | 325 | * Shape of the object, encodes the layout of the object's properties and |
michael@0 | 326 | * all other information about its structure. See vm/Shape.h. |
michael@0 | 327 | */ |
michael@0 | 328 | HeapPtrShape shape_; |
michael@0 | 329 | |
michael@0 | 330 | /* |
michael@0 | 331 | * The object's type and prototype. For objects with the LAZY_TYPE flag |
michael@0 | 332 | * set, this is the prototype's default 'new' type and can only be used |
michael@0 | 333 | * to get that prototype. |
michael@0 | 334 | */ |
michael@0 | 335 | HeapPtrTypeObject type_; |
michael@0 | 336 | |
michael@0 | 337 | HeapSlot *slots; /* Slots for object properties. */ |
michael@0 | 338 | HeapSlot *elements; /* Slots for object elements. */ |
michael@0 | 339 | |
michael@0 | 340 | friend bool |
michael@0 | 341 | ArraySetLength(JSContext *cx, Handle<ArrayObject*> obj, HandleId id, unsigned attrs, |
michael@0 | 342 | HandleValue value, bool setterIsStrict); |
michael@0 | 343 | |
michael@0 | 344 | private: |
michael@0 | 345 | static void staticAsserts() { |
michael@0 | 346 | static_assert(sizeof(ObjectImpl) == sizeof(shadow::Object), |
michael@0 | 347 | "shadow interface must match actual implementation"); |
michael@0 | 348 | static_assert(sizeof(ObjectImpl) % sizeof(Value) == 0, |
michael@0 | 349 | "fixed slots after an object must be aligned"); |
michael@0 | 350 | |
michael@0 | 351 | static_assert(offsetof(ObjectImpl, shape_) == offsetof(shadow::Object, shape), |
michael@0 | 352 | "shadow shape must match actual shape"); |
michael@0 | 353 | static_assert(offsetof(ObjectImpl, type_) == offsetof(shadow::Object, type), |
michael@0 | 354 | "shadow type must match actual type"); |
michael@0 | 355 | static_assert(offsetof(ObjectImpl, slots) == offsetof(shadow::Object, slots), |
michael@0 | 356 | "shadow slots must match actual slots"); |
michael@0 | 357 | static_assert(offsetof(ObjectImpl, elements) == offsetof(shadow::Object, _1), |
michael@0 | 358 | "shadow placeholder must match actual elements"); |
michael@0 | 359 | } |
michael@0 | 360 | |
michael@0 | 361 | JSObject * asObjectPtr() { return reinterpret_cast<JSObject *>(this); } |
michael@0 | 362 | const JSObject * asObjectPtr() const { return reinterpret_cast<const JSObject *>(this); } |
michael@0 | 363 | |
michael@0 | 364 | friend inline Value ObjectValue(ObjectImpl &obj); |
michael@0 | 365 | |
michael@0 | 366 | /* These functions are public, and they should remain public. */ |
michael@0 | 367 | |
michael@0 | 368 | public: |
michael@0 | 369 | TaggedProto getTaggedProto() const { |
michael@0 | 370 | return type_->proto(); |
michael@0 | 371 | } |
michael@0 | 372 | |
michael@0 | 373 | bool hasTenuredProto() const; |
michael@0 | 374 | |
michael@0 | 375 | const Class *getClass() const { |
michael@0 | 376 | return type_->clasp(); |
michael@0 | 377 | } |
michael@0 | 378 | |
michael@0 | 379 | static inline bool |
michael@0 | 380 | isExtensible(ExclusiveContext *cx, Handle<ObjectImpl*> obj, bool *extensible); |
michael@0 | 381 | |
michael@0 | 382 | // Indicates whether a non-proxy is extensible. Don't call on proxies! |
michael@0 | 383 | // This method really shouldn't exist -- but there are a few internal |
michael@0 | 384 | // places that want it (JITs and the like), and it'd be a pain to mark them |
michael@0 | 385 | // all as friends. |
michael@0 | 386 | bool nonProxyIsExtensible() const { |
michael@0 | 387 | MOZ_ASSERT(!isProxy()); |
michael@0 | 388 | |
michael@0 | 389 | // [[Extensible]] for ordinary non-proxy objects is an object flag. |
michael@0 | 390 | return !lastProperty()->hasObjectFlag(BaseShape::NOT_EXTENSIBLE); |
michael@0 | 391 | } |
michael@0 | 392 | |
michael@0 | 393 | #ifdef DEBUG |
michael@0 | 394 | bool isProxy() const; |
michael@0 | 395 | #endif |
michael@0 | 396 | |
michael@0 | 397 | // Attempt to change the [[Extensible]] bit on |obj| to false. Callers |
michael@0 | 398 | // must ensure that |obj| is currently extensible before calling this! |
michael@0 | 399 | static bool |
michael@0 | 400 | preventExtensions(JSContext *cx, Handle<ObjectImpl*> obj); |
michael@0 | 401 | |
michael@0 | 402 | HeapSlotArray getDenseElements() { |
michael@0 | 403 | JS_ASSERT(isNative()); |
michael@0 | 404 | return HeapSlotArray(elements); |
michael@0 | 405 | } |
michael@0 | 406 | const Value &getDenseElement(uint32_t idx) { |
michael@0 | 407 | JS_ASSERT(isNative()); |
michael@0 | 408 | MOZ_ASSERT(idx < getDenseInitializedLength()); |
michael@0 | 409 | return elements[idx]; |
michael@0 | 410 | } |
michael@0 | 411 | bool containsDenseElement(uint32_t idx) { |
michael@0 | 412 | JS_ASSERT(isNative()); |
michael@0 | 413 | return idx < getDenseInitializedLength() && !elements[idx].isMagic(JS_ELEMENTS_HOLE); |
michael@0 | 414 | } |
michael@0 | 415 | uint32_t getDenseInitializedLength() { |
michael@0 | 416 | JS_ASSERT(getClass()->isNative()); |
michael@0 | 417 | return getElementsHeader()->initializedLength; |
michael@0 | 418 | } |
michael@0 | 419 | uint32_t getDenseCapacity() { |
michael@0 | 420 | JS_ASSERT(getClass()->isNative()); |
michael@0 | 421 | return getElementsHeader()->capacity; |
michael@0 | 422 | } |
michael@0 | 423 | |
michael@0 | 424 | protected: |
michael@0 | 425 | #ifdef DEBUG |
michael@0 | 426 | void checkShapeConsistency(); |
michael@0 | 427 | #else |
michael@0 | 428 | void checkShapeConsistency() { } |
michael@0 | 429 | #endif |
michael@0 | 430 | |
michael@0 | 431 | Shape * |
michael@0 | 432 | replaceWithNewEquivalentShape(ThreadSafeContext *cx, |
michael@0 | 433 | Shape *existingShape, Shape *newShape = nullptr); |
michael@0 | 434 | |
michael@0 | 435 | enum GenerateShape { |
michael@0 | 436 | GENERATE_NONE, |
michael@0 | 437 | GENERATE_SHAPE |
michael@0 | 438 | }; |
michael@0 | 439 | |
michael@0 | 440 | bool setFlag(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag, |
michael@0 | 441 | GenerateShape generateShape = GENERATE_NONE); |
michael@0 | 442 | bool clearFlag(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag); |
michael@0 | 443 | |
michael@0 | 444 | bool toDictionaryMode(ThreadSafeContext *cx); |
michael@0 | 445 | |
michael@0 | 446 | private: |
michael@0 | 447 | friend class Nursery; |
michael@0 | 448 | |
michael@0 | 449 | /* |
michael@0 | 450 | * Get internal pointers to the range of values starting at start and |
michael@0 | 451 | * running for length. |
michael@0 | 452 | */ |
michael@0 | 453 | void getSlotRangeUnchecked(uint32_t start, uint32_t length, |
michael@0 | 454 | HeapSlot **fixedStart, HeapSlot **fixedEnd, |
michael@0 | 455 | HeapSlot **slotsStart, HeapSlot **slotsEnd) |
michael@0 | 456 | { |
michael@0 | 457 | MOZ_ASSERT(start + length >= start); |
michael@0 | 458 | |
michael@0 | 459 | uint32_t fixed = numFixedSlots(); |
michael@0 | 460 | if (start < fixed) { |
michael@0 | 461 | if (start + length < fixed) { |
michael@0 | 462 | *fixedStart = &fixedSlots()[start]; |
michael@0 | 463 | *fixedEnd = &fixedSlots()[start + length]; |
michael@0 | 464 | *slotsStart = *slotsEnd = nullptr; |
michael@0 | 465 | } else { |
michael@0 | 466 | uint32_t localCopy = fixed - start; |
michael@0 | 467 | *fixedStart = &fixedSlots()[start]; |
michael@0 | 468 | *fixedEnd = &fixedSlots()[start + localCopy]; |
michael@0 | 469 | *slotsStart = &slots[0]; |
michael@0 | 470 | *slotsEnd = &slots[length - localCopy]; |
michael@0 | 471 | } |
michael@0 | 472 | } else { |
michael@0 | 473 | *fixedStart = *fixedEnd = nullptr; |
michael@0 | 474 | *slotsStart = &slots[start - fixed]; |
michael@0 | 475 | *slotsEnd = &slots[start - fixed + length]; |
michael@0 | 476 | } |
michael@0 | 477 | } |
michael@0 | 478 | |
michael@0 | 479 | void getSlotRange(uint32_t start, uint32_t length, |
michael@0 | 480 | HeapSlot **fixedStart, HeapSlot **fixedEnd, |
michael@0 | 481 | HeapSlot **slotsStart, HeapSlot **slotsEnd) |
michael@0 | 482 | { |
michael@0 | 483 | MOZ_ASSERT(slotInRange(start + length, SENTINEL_ALLOWED)); |
michael@0 | 484 | getSlotRangeUnchecked(start, length, fixedStart, fixedEnd, slotsStart, slotsEnd); |
michael@0 | 485 | } |
michael@0 | 486 | |
michael@0 | 487 | protected: |
michael@0 | 488 | friend struct GCMarker; |
michael@0 | 489 | friend class Shape; |
michael@0 | 490 | friend class NewObjectCache; |
michael@0 | 491 | |
michael@0 | 492 | void invalidateSlotRange(uint32_t start, uint32_t length) { |
michael@0 | 493 | #ifdef DEBUG |
michael@0 | 494 | HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd; |
michael@0 | 495 | getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd); |
michael@0 | 496 | Debug_SetSlotRangeToCrashOnTouch(fixedStart, fixedEnd); |
michael@0 | 497 | Debug_SetSlotRangeToCrashOnTouch(slotsStart, slotsEnd); |
michael@0 | 498 | #endif /* DEBUG */ |
michael@0 | 499 | } |
michael@0 | 500 | |
michael@0 | 501 | void initializeSlotRange(uint32_t start, uint32_t count); |
michael@0 | 502 | |
michael@0 | 503 | /* |
michael@0 | 504 | * Initialize a flat array of slots to this object at a start slot. The |
michael@0 | 505 | * caller must ensure that are enough slots. |
michael@0 | 506 | */ |
michael@0 | 507 | void initSlotRange(uint32_t start, const Value *vector, uint32_t length); |
michael@0 | 508 | |
michael@0 | 509 | /* |
michael@0 | 510 | * Copy a flat array of slots to this object at a start slot. Caller must |
michael@0 | 511 | * ensure there are enough slots in this object. |
michael@0 | 512 | */ |
michael@0 | 513 | void copySlotRange(uint32_t start, const Value *vector, uint32_t length); |
michael@0 | 514 | |
michael@0 | 515 | #ifdef DEBUG |
michael@0 | 516 | enum SentinelAllowed { |
michael@0 | 517 | SENTINEL_NOT_ALLOWED, |
michael@0 | 518 | SENTINEL_ALLOWED |
michael@0 | 519 | }; |
michael@0 | 520 | |
michael@0 | 521 | /* |
michael@0 | 522 | * Check that slot is in range for the object's allocated slots. |
michael@0 | 523 | * If sentinelAllowed then slot may equal the slot capacity. |
michael@0 | 524 | */ |
michael@0 | 525 | bool slotInRange(uint32_t slot, SentinelAllowed sentinel = SENTINEL_NOT_ALLOWED) const; |
michael@0 | 526 | #endif |
michael@0 | 527 | |
michael@0 | 528 | /* |
michael@0 | 529 | * Minimum size for dynamically allocated slots in normal Objects. |
michael@0 | 530 | * ArrayObjects don't use this limit and can have a lower slot capacity, |
michael@0 | 531 | * since they normally don't have a lot of slots. |
michael@0 | 532 | */ |
michael@0 | 533 | static const uint32_t SLOT_CAPACITY_MIN = 8; |
michael@0 | 534 | |
michael@0 | 535 | HeapSlot *fixedSlots() const { |
michael@0 | 536 | return reinterpret_cast<HeapSlot *>(uintptr_t(this) + sizeof(ObjectImpl)); |
michael@0 | 537 | } |
michael@0 | 538 | |
michael@0 | 539 | /* |
michael@0 | 540 | * These functions are currently public for simplicity; in the long run |
michael@0 | 541 | * it may make sense to make at least some of them private. |
michael@0 | 542 | */ |
michael@0 | 543 | |
michael@0 | 544 | public: |
michael@0 | 545 | Shape * lastProperty() const { |
michael@0 | 546 | MOZ_ASSERT(shape_); |
michael@0 | 547 | return shape_; |
michael@0 | 548 | } |
michael@0 | 549 | |
michael@0 | 550 | bool generateOwnShape(ThreadSafeContext *cx, js::Shape *newShape = nullptr) { |
michael@0 | 551 | return replaceWithNewEquivalentShape(cx, lastProperty(), newShape); |
michael@0 | 552 | } |
michael@0 | 553 | |
michael@0 | 554 | JSCompartment *compartment() const { |
michael@0 | 555 | return lastProperty()->base()->compartment(); |
michael@0 | 556 | } |
michael@0 | 557 | |
michael@0 | 558 | bool isNative() const { |
michael@0 | 559 | return lastProperty()->isNative(); |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | types::TypeObject *type() const { |
michael@0 | 563 | MOZ_ASSERT(!hasLazyType()); |
michael@0 | 564 | return typeRaw(); |
michael@0 | 565 | } |
michael@0 | 566 | |
michael@0 | 567 | types::TypeObject *typeRaw() const { |
michael@0 | 568 | return type_; |
michael@0 | 569 | } |
michael@0 | 570 | |
michael@0 | 571 | uint32_t numFixedSlots() const { |
michael@0 | 572 | return reinterpret_cast<const shadow::Object *>(this)->numFixedSlots(); |
michael@0 | 573 | } |
michael@0 | 574 | |
michael@0 | 575 | /* |
michael@0 | 576 | * Whether this is the only object which has its specified type. This |
michael@0 | 577 | * object will have its type constructed lazily as needed by analysis. |
michael@0 | 578 | */ |
michael@0 | 579 | bool hasSingletonType() const { |
michael@0 | 580 | return !!type_->singleton(); |
michael@0 | 581 | } |
michael@0 | 582 | |
michael@0 | 583 | /* |
michael@0 | 584 | * Whether the object's type has not been constructed yet. If an object |
michael@0 | 585 | * might have a lazy type, use getType() below, otherwise type(). |
michael@0 | 586 | */ |
michael@0 | 587 | bool hasLazyType() const { |
michael@0 | 588 | return type_->lazy(); |
michael@0 | 589 | } |
michael@0 | 590 | |
michael@0 | 591 | uint32_t slotSpan() const { |
michael@0 | 592 | if (inDictionaryMode()) |
michael@0 | 593 | return lastProperty()->base()->slotSpan(); |
michael@0 | 594 | return lastProperty()->slotSpan(); |
michael@0 | 595 | } |
michael@0 | 596 | |
michael@0 | 597 | /* Compute dynamicSlotsCount() for this object. */ |
michael@0 | 598 | uint32_t numDynamicSlots() const { |
michael@0 | 599 | return dynamicSlotsCount(numFixedSlots(), slotSpan(), getClass()); |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | |
michael@0 | 603 | Shape *nativeLookup(ExclusiveContext *cx, jsid id); |
michael@0 | 604 | Shape *nativeLookup(ExclusiveContext *cx, PropertyName *name) { |
michael@0 | 605 | return nativeLookup(cx, NameToId(name)); |
michael@0 | 606 | } |
michael@0 | 607 | |
michael@0 | 608 | bool nativeContains(ExclusiveContext *cx, jsid id) { |
michael@0 | 609 | return nativeLookup(cx, id) != nullptr; |
michael@0 | 610 | } |
michael@0 | 611 | bool nativeContains(ExclusiveContext *cx, PropertyName* name) { |
michael@0 | 612 | return nativeLookup(cx, name) != nullptr; |
michael@0 | 613 | } |
michael@0 | 614 | bool nativeContains(ExclusiveContext *cx, Shape* shape) { |
michael@0 | 615 | return nativeLookup(cx, shape->propid()) == shape; |
michael@0 | 616 | } |
michael@0 | 617 | |
michael@0 | 618 | /* Contextless; can be called from parallel code. */ |
michael@0 | 619 | Shape *nativeLookupPure(jsid id); |
michael@0 | 620 | Shape *nativeLookupPure(PropertyName *name) { |
michael@0 | 621 | return nativeLookupPure(NameToId(name)); |
michael@0 | 622 | } |
michael@0 | 623 | |
michael@0 | 624 | bool nativeContainsPure(jsid id) { |
michael@0 | 625 | return nativeLookupPure(id) != nullptr; |
michael@0 | 626 | } |
michael@0 | 627 | bool nativeContainsPure(PropertyName* name) { |
michael@0 | 628 | return nativeContainsPure(NameToId(name)); |
michael@0 | 629 | } |
michael@0 | 630 | bool nativeContainsPure(Shape* shape) { |
michael@0 | 631 | return nativeLookupPure(shape->propid()) == shape; |
michael@0 | 632 | } |
michael@0 | 633 | |
michael@0 | 634 | const JSClass *getJSClass() const { |
michael@0 | 635 | return Jsvalify(getClass()); |
michael@0 | 636 | } |
michael@0 | 637 | bool hasClass(const Class *c) const { |
michael@0 | 638 | return getClass() == c; |
michael@0 | 639 | } |
michael@0 | 640 | const ObjectOps *getOps() const { |
michael@0 | 641 | return &getClass()->ops; |
michael@0 | 642 | } |
michael@0 | 643 | |
michael@0 | 644 | /* |
michael@0 | 645 | * An object is a delegate if it is on another object's prototype or scope |
michael@0 | 646 | * chain, and therefore the delegate might be asked implicitly to get or |
michael@0 | 647 | * set a property on behalf of another object. Delegates may be accessed |
michael@0 | 648 | * directly too, as may any object, but only those objects linked after the |
michael@0 | 649 | * head of any prototype or scope chain are flagged as delegates. This |
michael@0 | 650 | * definition helps to optimize shape-based property cache invalidation |
michael@0 | 651 | * (see Purge{Scope,Proto}Chain in jsobj.cpp). |
michael@0 | 652 | */ |
michael@0 | 653 | bool isDelegate() const { |
michael@0 | 654 | return lastProperty()->hasObjectFlag(BaseShape::DELEGATE); |
michael@0 | 655 | } |
michael@0 | 656 | |
michael@0 | 657 | /* |
michael@0 | 658 | * Return true if this object is a native one that has been converted from |
michael@0 | 659 | * shared-immutable prototype-rooted shape storage to dictionary-shapes in |
michael@0 | 660 | * a doubly-linked list. |
michael@0 | 661 | */ |
michael@0 | 662 | bool inDictionaryMode() const { |
michael@0 | 663 | return lastProperty()->inDictionary(); |
michael@0 | 664 | } |
michael@0 | 665 | |
michael@0 | 666 | const Value &getSlot(uint32_t slot) const { |
michael@0 | 667 | MOZ_ASSERT(slotInRange(slot)); |
michael@0 | 668 | uint32_t fixed = numFixedSlots(); |
michael@0 | 669 | if (slot < fixed) |
michael@0 | 670 | return fixedSlots()[slot]; |
michael@0 | 671 | return slots[slot - fixed]; |
michael@0 | 672 | } |
michael@0 | 673 | |
michael@0 | 674 | const HeapSlot *getSlotAddressUnchecked(uint32_t slot) const { |
michael@0 | 675 | uint32_t fixed = numFixedSlots(); |
michael@0 | 676 | if (slot < fixed) |
michael@0 | 677 | return fixedSlots() + slot; |
michael@0 | 678 | return slots + (slot - fixed); |
michael@0 | 679 | } |
michael@0 | 680 | |
michael@0 | 681 | HeapSlot *getSlotAddressUnchecked(uint32_t slot) { |
michael@0 | 682 | const ObjectImpl *obj = static_cast<const ObjectImpl*>(this); |
michael@0 | 683 | return const_cast<HeapSlot*>(obj->getSlotAddressUnchecked(slot)); |
michael@0 | 684 | } |
michael@0 | 685 | |
michael@0 | 686 | HeapSlot *getSlotAddress(uint32_t slot) { |
michael@0 | 687 | /* |
michael@0 | 688 | * This can be used to get the address of the end of the slots for the |
michael@0 | 689 | * object, which may be necessary when fetching zero-length arrays of |
michael@0 | 690 | * slots (e.g. for callObjVarArray). |
michael@0 | 691 | */ |
michael@0 | 692 | MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED)); |
michael@0 | 693 | return getSlotAddressUnchecked(slot); |
michael@0 | 694 | } |
michael@0 | 695 | |
michael@0 | 696 | const HeapSlot *getSlotAddress(uint32_t slot) const { |
michael@0 | 697 | /* |
michael@0 | 698 | * This can be used to get the address of the end of the slots for the |
michael@0 | 699 | * object, which may be necessary when fetching zero-length arrays of |
michael@0 | 700 | * slots (e.g. for callObjVarArray). |
michael@0 | 701 | */ |
michael@0 | 702 | MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED)); |
michael@0 | 703 | return getSlotAddressUnchecked(slot); |
michael@0 | 704 | } |
michael@0 | 705 | |
michael@0 | 706 | HeapSlot &getSlotRef(uint32_t slot) { |
michael@0 | 707 | MOZ_ASSERT(slotInRange(slot)); |
michael@0 | 708 | return *getSlotAddress(slot); |
michael@0 | 709 | } |
michael@0 | 710 | |
michael@0 | 711 | const HeapSlot &getSlotRef(uint32_t slot) const { |
michael@0 | 712 | MOZ_ASSERT(slotInRange(slot)); |
michael@0 | 713 | return *getSlotAddress(slot); |
michael@0 | 714 | } |
michael@0 | 715 | |
michael@0 | 716 | HeapSlot &nativeGetSlotRef(uint32_t slot) { |
michael@0 | 717 | JS_ASSERT(isNative() && slot < slotSpan()); |
michael@0 | 718 | return getSlotRef(slot); |
michael@0 | 719 | } |
michael@0 | 720 | const Value &nativeGetSlot(uint32_t slot) const { |
michael@0 | 721 | JS_ASSERT(isNative() && slot < slotSpan()); |
michael@0 | 722 | return getSlot(slot); |
michael@0 | 723 | } |
michael@0 | 724 | |
michael@0 | 725 | void setSlot(uint32_t slot, const Value &value) { |
michael@0 | 726 | MOZ_ASSERT(slotInRange(slot)); |
michael@0 | 727 | MOZ_ASSERT(IsObjectValueInCompartment(value, compartment())); |
michael@0 | 728 | getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value); |
michael@0 | 729 | } |
michael@0 | 730 | |
michael@0 | 731 | inline void setCrossCompartmentSlot(uint32_t slot, const Value &value) { |
michael@0 | 732 | MOZ_ASSERT(slotInRange(slot)); |
michael@0 | 733 | getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value); |
michael@0 | 734 | } |
michael@0 | 735 | |
michael@0 | 736 | void initSlot(uint32_t slot, const Value &value) { |
michael@0 | 737 | MOZ_ASSERT(getSlot(slot).isUndefined()); |
michael@0 | 738 | MOZ_ASSERT(slotInRange(slot)); |
michael@0 | 739 | MOZ_ASSERT(IsObjectValueInCompartment(value, compartment())); |
michael@0 | 740 | initSlotUnchecked(slot, value); |
michael@0 | 741 | } |
michael@0 | 742 | |
michael@0 | 743 | void initCrossCompartmentSlot(uint32_t slot, const Value &value) { |
michael@0 | 744 | MOZ_ASSERT(getSlot(slot).isUndefined()); |
michael@0 | 745 | MOZ_ASSERT(slotInRange(slot)); |
michael@0 | 746 | initSlotUnchecked(slot, value); |
michael@0 | 747 | } |
michael@0 | 748 | |
michael@0 | 749 | void initSlotUnchecked(uint32_t slot, const Value &value) { |
michael@0 | 750 | getSlotAddressUnchecked(slot)->init(this->asObjectPtr(), HeapSlot::Slot, slot, value); |
michael@0 | 751 | } |
michael@0 | 752 | |
michael@0 | 753 | /* For slots which are known to always be fixed, due to the way they are allocated. */ |
michael@0 | 754 | |
michael@0 | 755 | HeapSlot &getFixedSlotRef(uint32_t slot) { |
michael@0 | 756 | MOZ_ASSERT(slot < numFixedSlots()); |
michael@0 | 757 | return fixedSlots()[slot]; |
michael@0 | 758 | } |
michael@0 | 759 | |
michael@0 | 760 | const Value &getFixedSlot(uint32_t slot) const { |
michael@0 | 761 | MOZ_ASSERT(slot < numFixedSlots()); |
michael@0 | 762 | return fixedSlots()[slot]; |
michael@0 | 763 | } |
michael@0 | 764 | |
michael@0 | 765 | void setFixedSlot(uint32_t slot, const Value &value) { |
michael@0 | 766 | MOZ_ASSERT(slot < numFixedSlots()); |
michael@0 | 767 | fixedSlots()[slot].set(this->asObjectPtr(), HeapSlot::Slot, slot, value); |
michael@0 | 768 | } |
michael@0 | 769 | |
michael@0 | 770 | void initFixedSlot(uint32_t slot, const Value &value) { |
michael@0 | 771 | MOZ_ASSERT(slot < numFixedSlots()); |
michael@0 | 772 | fixedSlots()[slot].init(this->asObjectPtr(), HeapSlot::Slot, slot, value); |
michael@0 | 773 | } |
michael@0 | 774 | |
michael@0 | 775 | /* |
michael@0 | 776 | * Get the number of dynamic slots to allocate to cover the properties in |
michael@0 | 777 | * an object with the given number of fixed slots and slot span. The slot |
michael@0 | 778 | * capacity is not stored explicitly, and the allocated size of the slot |
michael@0 | 779 | * array is kept in sync with this count. |
michael@0 | 780 | */ |
michael@0 | 781 | static uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class *clasp); |
michael@0 | 782 | |
michael@0 | 783 | /* Memory usage functions. */ |
michael@0 | 784 | size_t tenuredSizeOfThis() const { |
michael@0 | 785 | return js::gc::Arena::thingSize(tenuredGetAllocKind()); |
michael@0 | 786 | } |
michael@0 | 787 | |
michael@0 | 788 | /* Elements accessors. */ |
michael@0 | 789 | |
michael@0 | 790 | ObjectElements * getElementsHeader() const { |
michael@0 | 791 | return ObjectElements::fromElements(elements); |
michael@0 | 792 | } |
michael@0 | 793 | |
michael@0 | 794 | inline HeapSlot *fixedElements() const { |
michael@0 | 795 | static_assert(2 * sizeof(Value) == sizeof(ObjectElements), |
michael@0 | 796 | "when elements are stored inline, the first two " |
michael@0 | 797 | "slots will hold the ObjectElements header"); |
michael@0 | 798 | return &fixedSlots()[2]; |
michael@0 | 799 | } |
michael@0 | 800 | |
michael@0 | 801 | #ifdef DEBUG |
michael@0 | 802 | bool canHaveNonEmptyElements(); |
michael@0 | 803 | #endif |
michael@0 | 804 | |
michael@0 | 805 | void setFixedElements() { |
michael@0 | 806 | JS_ASSERT(canHaveNonEmptyElements()); |
michael@0 | 807 | this->elements = fixedElements(); |
michael@0 | 808 | } |
michael@0 | 809 | |
michael@0 | 810 | inline bool hasDynamicElements() const { |
michael@0 | 811 | /* |
michael@0 | 812 | * Note: for objects with zero fixed slots this could potentially give |
michael@0 | 813 | * a spurious 'true' result, if the end of this object is exactly |
michael@0 | 814 | * aligned with the end of its arena and dynamic slots are allocated |
michael@0 | 815 | * immediately afterwards. Such cases cannot occur for dense arrays |
michael@0 | 816 | * (which have at least two fixed slots) and can only result in a leak. |
michael@0 | 817 | */ |
michael@0 | 818 | return !hasEmptyElements() && elements != fixedElements(); |
michael@0 | 819 | } |
michael@0 | 820 | |
michael@0 | 821 | inline bool hasFixedElements() const { |
michael@0 | 822 | return elements == fixedElements(); |
michael@0 | 823 | } |
michael@0 | 824 | |
michael@0 | 825 | inline bool hasEmptyElements() const { |
michael@0 | 826 | return elements == emptyObjectElements; |
michael@0 | 827 | } |
michael@0 | 828 | |
michael@0 | 829 | /* |
michael@0 | 830 | * Get a pointer to the unused data in the object's allocation immediately |
michael@0 | 831 | * following this object, for use with objects which allocate a larger size |
michael@0 | 832 | * class than they need and store non-elements data inline. |
michael@0 | 833 | */ |
michael@0 | 834 | inline void *fixedData(size_t nslots) const; |
michael@0 | 835 | |
michael@0 | 836 | /* GC support. */ |
michael@0 | 837 | static ThingRootKind rootKind() { return THING_ROOT_OBJECT; } |
michael@0 | 838 | |
michael@0 | 839 | inline void privateWriteBarrierPre(void **oldval); |
michael@0 | 840 | |
michael@0 | 841 | void privateWriteBarrierPost(void **pprivate) { |
michael@0 | 842 | #ifdef JSGC_GENERATIONAL |
michael@0 | 843 | shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell(reinterpret_cast<js::gc::Cell **>(pprivate)); |
michael@0 | 844 | #endif |
michael@0 | 845 | } |
michael@0 | 846 | |
michael@0 | 847 | void markChildren(JSTracer *trc); |
michael@0 | 848 | |
michael@0 | 849 | /* Private data accessors. */ |
michael@0 | 850 | |
michael@0 | 851 | inline void *&privateRef(uint32_t nfixed) const { /* XXX should be private, not protected! */ |
michael@0 | 852 | /* |
michael@0 | 853 | * The private pointer of an object can hold any word sized value. |
michael@0 | 854 | * Private pointers are stored immediately after the last fixed slot of |
michael@0 | 855 | * the object. |
michael@0 | 856 | */ |
michael@0 | 857 | MOZ_ASSERT(nfixed == numFixedSlots()); |
michael@0 | 858 | MOZ_ASSERT(hasPrivate()); |
michael@0 | 859 | HeapSlot *end = &fixedSlots()[nfixed]; |
michael@0 | 860 | return *reinterpret_cast<void**>(end); |
michael@0 | 861 | } |
michael@0 | 862 | |
michael@0 | 863 | bool hasPrivate() const { |
michael@0 | 864 | return getClass()->hasPrivate(); |
michael@0 | 865 | } |
michael@0 | 866 | void *getPrivate() const { |
michael@0 | 867 | return privateRef(numFixedSlots()); |
michael@0 | 868 | } |
michael@0 | 869 | void setPrivate(void *data) { |
michael@0 | 870 | void **pprivate = &privateRef(numFixedSlots()); |
michael@0 | 871 | privateWriteBarrierPre(pprivate); |
michael@0 | 872 | *pprivate = data; |
michael@0 | 873 | } |
michael@0 | 874 | |
michael@0 | 875 | void setPrivateGCThing(gc::Cell *cell) { |
michael@0 | 876 | void **pprivate = &privateRef(numFixedSlots()); |
michael@0 | 877 | privateWriteBarrierPre(pprivate); |
michael@0 | 878 | *pprivate = reinterpret_cast<void *>(cell); |
michael@0 | 879 | privateWriteBarrierPost(pprivate); |
michael@0 | 880 | } |
michael@0 | 881 | |
michael@0 | 882 | void setPrivateUnbarriered(void *data) { |
michael@0 | 883 | void **pprivate = &privateRef(numFixedSlots()); |
michael@0 | 884 | *pprivate = data; |
michael@0 | 885 | } |
michael@0 | 886 | void initPrivate(void *data) { |
michael@0 | 887 | privateRef(numFixedSlots()) = data; |
michael@0 | 888 | } |
michael@0 | 889 | |
michael@0 | 890 | /* Access private data for an object with a known number of fixed slots. */ |
michael@0 | 891 | inline void *getPrivate(uint32_t nfixed) const { |
michael@0 | 892 | return privateRef(nfixed); |
michael@0 | 893 | } |
michael@0 | 894 | |
michael@0 | 895 | /* GC Accessors */ |
michael@0 | 896 | void setInitialSlots(HeapSlot *newSlots) { slots = newSlots; } |
michael@0 | 897 | |
michael@0 | 898 | /* JIT Accessors */ |
michael@0 | 899 | static size_t offsetOfShape() { return offsetof(ObjectImpl, shape_); } |
michael@0 | 900 | HeapPtrShape *addressOfShape() { return &shape_; } |
michael@0 | 901 | |
michael@0 | 902 | static size_t offsetOfType() { return offsetof(ObjectImpl, type_); } |
michael@0 | 903 | HeapPtrTypeObject *addressOfType() { return &type_; } |
michael@0 | 904 | |
michael@0 | 905 | static size_t offsetOfElements() { return offsetof(ObjectImpl, elements); } |
michael@0 | 906 | static size_t offsetOfFixedElements() { |
michael@0 | 907 | return sizeof(ObjectImpl) + sizeof(ObjectElements); |
michael@0 | 908 | } |
michael@0 | 909 | |
michael@0 | 910 | static size_t getFixedSlotOffset(size_t slot) { |
michael@0 | 911 | return sizeof(ObjectImpl) + slot * sizeof(Value); |
michael@0 | 912 | } |
michael@0 | 913 | static size_t getPrivateDataOffset(size_t nfixed) { return getFixedSlotOffset(nfixed); } |
michael@0 | 914 | static size_t offsetOfSlots() { return offsetof(ObjectImpl, slots); } |
michael@0 | 915 | }; |
michael@0 | 916 | |
michael@0 | 917 | namespace gc { |
michael@0 | 918 | |
michael@0 | 919 | template <> |
michael@0 | 920 | MOZ_ALWAYS_INLINE Zone * |
michael@0 | 921 | BarrieredCell<ObjectImpl>::zone() const |
michael@0 | 922 | { |
michael@0 | 923 | const ObjectImpl* obj = static_cast<const ObjectImpl*>(this); |
michael@0 | 924 | JS::Zone *zone = obj->shape_->zone(); |
michael@0 | 925 | JS_ASSERT(CurrentThreadCanAccessZone(zone)); |
michael@0 | 926 | return zone; |
michael@0 | 927 | } |
michael@0 | 928 | |
michael@0 | 929 | template <> |
michael@0 | 930 | MOZ_ALWAYS_INLINE Zone * |
michael@0 | 931 | BarrieredCell<ObjectImpl>::zoneFromAnyThread() const |
michael@0 | 932 | { |
michael@0 | 933 | const ObjectImpl* obj = static_cast<const ObjectImpl*>(this); |
michael@0 | 934 | return obj->shape_->zoneFromAnyThread(); |
michael@0 | 935 | } |
michael@0 | 936 | |
michael@0 | 937 | // TypeScript::global uses 0x1 as a special value. |
michael@0 | 938 | template<> |
michael@0 | 939 | /* static */ inline bool |
michael@0 | 940 | BarrieredCell<ObjectImpl>::isNullLike(ObjectImpl *obj) |
michael@0 | 941 | { |
michael@0 | 942 | return IsNullTaggedPointer(obj); |
michael@0 | 943 | } |
michael@0 | 944 | |
michael@0 | 945 | template<> |
michael@0 | 946 | /* static */ inline void |
michael@0 | 947 | BarrieredCell<ObjectImpl>::writeBarrierPost(ObjectImpl *obj, void *addr) |
michael@0 | 948 | { |
michael@0 | 949 | #ifdef JSGC_GENERATIONAL |
michael@0 | 950 | if (IsNullTaggedPointer(obj)) |
michael@0 | 951 | return; |
michael@0 | 952 | obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell((Cell **)addr); |
michael@0 | 953 | #endif |
michael@0 | 954 | } |
michael@0 | 955 | |
michael@0 | 956 | template<> |
michael@0 | 957 | /* static */ inline void |
michael@0 | 958 | BarrieredCell<ObjectImpl>::writeBarrierPostRelocate(ObjectImpl *obj, void *addr) |
michael@0 | 959 | { |
michael@0 | 960 | #ifdef JSGC_GENERATIONAL |
michael@0 | 961 | obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putRelocatableCell((Cell **)addr); |
michael@0 | 962 | #endif |
michael@0 | 963 | } |
michael@0 | 964 | |
michael@0 | 965 | template<> |
michael@0 | 966 | /* static */ inline void |
michael@0 | 967 | BarrieredCell<ObjectImpl>::writeBarrierPostRemove(ObjectImpl *obj, void *addr) |
michael@0 | 968 | { |
michael@0 | 969 | #ifdef JSGC_GENERATIONAL |
michael@0 | 970 | obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->removeRelocatableCell((Cell **)addr); |
michael@0 | 971 | #endif |
michael@0 | 972 | } |
michael@0 | 973 | |
michael@0 | 974 | } // namespace gc |
michael@0 | 975 | |
michael@0 | 976 | inline void |
michael@0 | 977 | ObjectImpl::privateWriteBarrierPre(void **oldval) |
michael@0 | 978 | { |
michael@0 | 979 | #ifdef JSGC_INCREMENTAL |
michael@0 | 980 | JS::shadow::Zone *shadowZone = this->shadowZoneFromAnyThread(); |
michael@0 | 981 | if (shadowZone->needsBarrier()) { |
michael@0 | 982 | if (*oldval && getClass()->trace) |
michael@0 | 983 | getClass()->trace(shadowZone->barrierTracer(), this->asObjectPtr()); |
michael@0 | 984 | } |
michael@0 | 985 | #endif |
michael@0 | 986 | } |
michael@0 | 987 | |
michael@0 | 988 | inline Value |
michael@0 | 989 | ObjectValue(ObjectImpl &obj) |
michael@0 | 990 | { |
michael@0 | 991 | Value v; |
michael@0 | 992 | v.setObject(*obj.asObjectPtr()); |
michael@0 | 993 | return v; |
michael@0 | 994 | } |
michael@0 | 995 | |
michael@0 | 996 | inline Handle<JSObject*> |
michael@0 | 997 | Downcast(Handle<ObjectImpl*> obj) |
michael@0 | 998 | { |
michael@0 | 999 | return Handle<JSObject*>::fromMarkedLocation(reinterpret_cast<JSObject* const*>(obj.address())); |
michael@0 | 1000 | } |
michael@0 | 1001 | |
michael@0 | 1002 | #ifdef DEBUG |
michael@0 | 1003 | static inline bool |
michael@0 | 1004 | IsObjectValueInCompartment(js::Value v, JSCompartment *comp) |
michael@0 | 1005 | { |
michael@0 | 1006 | if (!v.isObject()) |
michael@0 | 1007 | return true; |
michael@0 | 1008 | return reinterpret_cast<ObjectImpl*>(&v.toObject())->compartment() == comp; |
michael@0 | 1009 | } |
michael@0 | 1010 | #endif |
michael@0 | 1011 | |
michael@0 | 1012 | } /* namespace js */ |
michael@0 | 1013 | |
michael@0 | 1014 | #endif /* vm_ObjectImpl_h */ |