Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef vm_Shape_h
8 : #define vm_Shape_h
9 :
10 : #include "mozilla/Attributes.h"
11 : #include "mozilla/GuardObjects.h"
12 : #include "mozilla/MathAlgorithms.h"
13 : #include "mozilla/Maybe.h"
14 : #include "mozilla/MemoryReporting.h"
15 : #include "mozilla/TemplateLib.h"
16 :
17 : #include "jsapi.h"
18 : #include "jsatom.h"
19 : #include "jsfriendapi.h"
20 : #include "jstypes.h"
21 : #include "NamespaceImports.h"
22 :
23 : #include "gc/Barrier.h"
24 : #include "gc/Heap.h"
25 : #include "gc/Marking.h"
26 : #include "gc/Rooting.h"
27 : #include "js/HashTable.h"
28 : #include "js/MemoryMetrics.h"
29 : #include "js/RootingAPI.h"
30 : #include "js/UbiNode.h"
31 : #include "vm/ObjectGroup.h"
32 : #include "vm/String.h"
33 : #include "vm/Symbol.h"
34 :
35 : #ifdef _MSC_VER
36 : #pragma warning(push)
37 : #pragma warning(disable:4800)
38 : #pragma warning(push)
39 : #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
40 : #endif
41 :
42 : /*
43 : * In isolation, a Shape represents a property that exists in one or more
44 : * objects; it has an id, flags, etc. (But it doesn't represent the property's
45 : * value.) However, Shapes are always stored in linked linear sequence of
46 : * Shapes, called "shape lineages". Each shape lineage represents the layout of
47 : * an entire object.
48 : *
49 : * Every JSObject has a pointer, |shape_|, accessible via lastProperty(), to
50 : * the last Shape in a shape lineage, which identifies the property most
51 : * recently added to the object. This pointer permits fast object layout
52 : * tests. The shape lineage order also dictates the enumeration order for the
53 : * object; ECMA requires no particular order but this implementation has
54 : * promised and delivered property definition order.
55 : *
56 : * Shape lineages occur in two kinds of data structure.
57 : *
58 : * 1. N-ary property trees. Each path from a non-root node to the root node in
59 : * a property tree is a shape lineage. Property trees permit full (or
60 : * partial) sharing of Shapes between objects that have fully (or partly)
61 : * identical layouts. The root is an EmptyShape whose identity is determined
62 : * by the object's class, compartment and prototype. These Shapes are shared
63 : * and immutable.
64 : *
65 : * 2. Dictionary mode lists. Shapes in such lists are said to be "in
66 : * dictionary mode", as are objects that point to such Shapes. These Shapes
67 : * are unshared, private to a single object, and immutable except for their
68 : * links in the dictionary list.
69 : *
70 : * All shape lineages are bi-directionally linked, via the |parent| and
71 : * |kids|/|listp| members.
72 : *
73 : * Shape lineages start out life in the property tree. They can be converted
74 : * (by copying) to dictionary mode lists in the following circumstances.
75 : *
76 : * 1. The shape lineage's size reaches MAX_HEIGHT. This reasonable limit avoids
77 : * potential worst cases involving shape lineage mutations.
78 : *
79 : * 2. A property represented by a non-last Shape in a shape lineage is removed
80 : * from an object. (In the last Shape case, obj->shape_ can be easily
81 : * adjusted to point to obj->shape_->parent.) We originally tried lazy
82 : * forking of the property tree, but this blows up for delete/add
83 : * repetitions.
84 : *
85 : * 3. A property represented by a non-last Shape in a shape lineage has its
86 : * attributes modified.
87 : *
88 : * To find the Shape for a particular property of an object initially requires
89 : * a linear search. But if the number of searches starting at any particular
90 : * Shape in the property tree exceeds LINEAR_SEARCHES_MAX and the Shape's
91 : * lineage has (excluding the EmptyShape) at least MIN_ENTRIES, we create an
92 : * auxiliary hash table -- the ShapeTable -- that allows faster lookup.
93 : * Furthermore, a ShapeTable is always created for dictionary mode lists,
94 : * and it is attached to the last Shape in the lineage. Shape tables for
95 : * property tree Shapes never change, but shape tables for dictionary mode
96 : * Shapes can grow and shrink.
97 : *
98 : * To save memory, shape tables can be discarded on GC and recreated when
99 : * needed. AutoKeepShapeTables can be used to avoid discarding shape tables
100 : * for a particular zone. Methods operating on ShapeTables take either an
101 : * AutoCheckCannotGC or AutoKeepShapeTables argument, to help ensure tables
102 : * are not purged while we're using them.
103 : *
104 : * There used to be a long, math-heavy comment here explaining why property
105 : * trees are more space-efficient than alternatives. This was removed in bug
106 : * 631138; see that bug for the full details.
107 : *
108 : * For getters/setters, an AccessorShape is allocated. This is a slightly fatter
109 : * type with extra fields for the getter/setter data.
110 : *
111 : * Because many Shapes have similar data, there is actually a secondary type
112 : * called a BaseShape that holds some of a Shape's data. Many shapes can share
113 : * a single BaseShape.
114 : */
115 :
116 : MOZ_ALWAYS_INLINE size_t
117 3824753 : JSSLOT_FREE(const js::Class* clasp)
118 : {
119 : // Proxy classes have reserved slots, but proxies manage their own slot
120 : // layout.
121 3824753 : MOZ_ASSERT(!clasp->isProxy());
122 3824761 : return JSCLASS_RESERVED_SLOTS(clasp);
123 : }
124 :
125 : namespace js {
126 :
127 : class Shape;
128 : struct StackShape;
129 :
130 : struct ShapeHasher : public DefaultHasher<Shape*> {
131 : typedef Shape* Key;
132 : typedef StackShape Lookup;
133 :
134 : static MOZ_ALWAYS_INLINE HashNumber hash(const Lookup& l);
135 : static MOZ_ALWAYS_INLINE bool match(Key k, const Lookup& l);
136 : };
137 :
138 : typedef HashSet<Shape*, ShapeHasher, SystemAllocPolicy> KidsHash;
139 :
140 : class KidsPointer {
141 : private:
142 : enum {
143 : SHAPE = 0,
144 : HASH = 1,
145 : TAG = 1
146 : };
147 :
148 : uintptr_t w;
149 :
150 : public:
151 269003 : bool isNull() const { return !w; }
152 106036 : void setNull() { w = 0; }
153 :
154 225742 : bool isShape() const { return (w & TAG) == SHAPE && !isNull(); }
155 67125 : Shape* toShape() const {
156 67125 : MOZ_ASSERT(isShape());
157 67125 : return reinterpret_cast<Shape*>(w & ~uintptr_t(TAG));
158 : }
159 62163 : void setShape(Shape* shape) {
160 62163 : MOZ_ASSERT(shape);
161 62163 : MOZ_ASSERT((reinterpret_cast<uintptr_t>(static_cast<Shape*>(shape)) & TAG) == 0);
162 62163 : w = reinterpret_cast<uintptr_t>(static_cast<Shape*>(shape)) | SHAPE;
163 62163 : }
164 :
165 117637 : bool isHash() const { return (w & TAG) == HASH; }
166 30277 : KidsHash* toHash() const {
167 30277 : MOZ_ASSERT(isHash());
168 30277 : return reinterpret_cast<KidsHash*>(w & ~uintptr_t(TAG));
169 : }
170 2095 : void setHash(KidsHash* hash) {
171 2095 : MOZ_ASSERT(hash);
172 2095 : MOZ_ASSERT((reinterpret_cast<uintptr_t>(hash) & TAG) == 0);
173 2095 : w = reinterpret_cast<uintptr_t>(hash) | HASH;
174 2095 : }
175 :
176 : #ifdef DEBUG
177 : void checkConsistency(Shape* aKid) const;
178 : #endif
179 : };
180 :
181 : class PropertyTree
182 : {
183 : friend class ::JSFunction;
184 :
185 : #ifdef DEBUG
186 : JS::Zone* zone_;
187 : #endif
188 :
189 : bool insertChild(JSContext* cx, Shape* parent, Shape* child);
190 :
191 : PropertyTree();
192 :
193 : public:
194 : /*
195 : * Use a lower limit for objects that are accessed using SETELEM (o[x] = y).
196 : * These objects are likely used as hashmaps and dictionary mode is more
197 : * efficient in this case.
198 : */
199 : enum {
200 : MAX_HEIGHT = 512,
201 : MAX_HEIGHT_WITH_ELEMENTS_ACCESS = 128
202 : };
203 :
204 31 : explicit PropertyTree(JS::Zone* zone)
205 : #ifdef DEBUG
206 31 : : zone_(zone)
207 : #endif
208 : {
209 31 : }
210 :
211 : MOZ_ALWAYS_INLINE Shape* inlinedGetChild(JSContext* cx, Shape* parent,
212 : JS::Handle<StackShape> child);
213 : Shape* getChild(JSContext* cx, Shape* parent, JS::Handle<StackShape> child);
214 : };
215 :
216 : class TenuringTracer;
217 :
218 : typedef JSGetterOp GetterOp;
219 : typedef JSSetterOp SetterOp;
220 :
221 : /* Limit on the number of slotful properties in an object. */
222 : static const uint32_t SHAPE_INVALID_SLOT = JS_BIT(24) - 1;
223 : static const uint32_t SHAPE_MAXIMUM_SLOT = JS_BIT(24) - 2;
224 :
225 : enum class MaybeAdding { Adding = true, NotAdding = false };
226 :
227 : class AutoKeepShapeTables;
228 :
229 : /*
230 : * Shapes use multiplicative hashing, but specialized to
231 : * minimize footprint.
232 : */
233 : class ShapeTable {
234 : public:
235 : friend class NativeObject;
236 : friend class BaseShape;
237 : static const uint32_t MIN_ENTRIES = 11;
238 :
239 : class Entry {
240 : // js::Shape pointer tag bit indicating a collision.
241 : static const uintptr_t SHAPE_COLLISION = 1;
242 : static Shape* const SHAPE_REMOVED; // = SHAPE_COLLISION
243 :
244 : Shape* shape_;
245 :
246 : Entry() = delete;
247 : Entry(const Entry&) = delete;
248 : Entry& operator=(const Entry&) = delete;
249 :
250 : public:
251 537179 : bool isFree() const { return shape_ == nullptr; }
252 257621 : bool isRemoved() const { return shape_ == SHAPE_REMOVED; }
253 10531 : bool isLive() const { return !isFree() && !isRemoved(); }
254 320657 : bool hadCollision() const { return uintptr_t(shape_) & SHAPE_COLLISION; }
255 :
256 242 : void setFree() { shape_ = nullptr; }
257 139 : void setRemoved() { shape_ = SHAPE_REMOVED; }
258 :
259 611171 : Shape* shape() const {
260 611171 : return reinterpret_cast<Shape*>(uintptr_t(shape_) & ~SHAPE_COLLISION);
261 : }
262 :
263 2564 : void setShape(Shape* shape) {
264 2564 : MOZ_ASSERT(isFree());
265 2564 : MOZ_ASSERT(shape);
266 2564 : MOZ_ASSERT(shape != SHAPE_REMOVED);
267 2564 : shape_ = shape;
268 2564 : MOZ_ASSERT(!hadCollision());
269 2564 : }
270 :
271 22914 : void flagCollision() {
272 22914 : shape_ = reinterpret_cast<Shape*>(uintptr_t(shape_) | SHAPE_COLLISION);
273 22914 : }
274 71769 : void setPreservingCollision(Shape* shape) {
275 71769 : shape_ = reinterpret_cast<Shape*>(uintptr_t(shape) | uintptr_t(hadCollision()));
276 71769 : }
277 : };
278 :
279 : private:
280 : static const uint32_t HASH_BITS = mozilla::tl::BitSize<HashNumber>::value;
281 :
282 : // This value is low because it's common for a ShapeTable to be created
283 : // with an entryCount of zero.
284 : static const uint32_t MIN_SIZE_LOG2 = 2;
285 : static const uint32_t MIN_SIZE = JS_BIT(MIN_SIZE_LOG2);
286 :
287 : uint32_t hashShift_; /* multiplicative hash shift */
288 :
289 : uint32_t entryCount_; /* number of entries in table */
290 : uint32_t removedCount_; /* removed entry sentinels in table */
291 :
292 : uint32_t freeList_; /* SHAPE_INVALID_SLOT or head of slot
293 : freelist in owning dictionary-mode
294 : object */
295 :
296 : Entry* entries_; /* table of ptrs to shared tree nodes */
297 :
298 : template<MaybeAdding Adding>
299 : MOZ_ALWAYS_INLINE Entry& searchUnchecked(jsid id);
300 :
301 : public:
302 2716 : explicit ShapeTable(uint32_t nentries)
303 2716 : : hashShift_(HASH_BITS - MIN_SIZE_LOG2),
304 : entryCount_(nentries),
305 : removedCount_(0),
306 : freeList_(SHAPE_INVALID_SLOT),
307 2716 : entries_(nullptr)
308 : {
309 : /* NB: entries is set by init, which must be called. */
310 2716 : }
311 :
312 0 : ~ShapeTable() {
313 0 : js_free(entries_);
314 0 : }
315 :
316 18283 : uint32_t entryCount() const { return entryCount_; }
317 :
318 1989 : uint32_t freeList() const { return freeList_; }
319 54 : void setFreeList(uint32_t slot) { freeList_ = slot; }
320 :
321 : /*
322 : * This counts the ShapeTable object itself (which must be
323 : * heap-allocated) and its |entries| array.
324 : */
325 0 : size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
326 0 : return mallocSizeOf(this) + mallocSizeOf(entries_);
327 : }
328 :
329 : // init() is fallible and reports OOM to the context.
330 : bool init(JSContext* cx, Shape* lastProp);
331 :
332 : // change() is fallible but does not report OOM.
333 : bool change(JSContext* cx, int log2Delta);
334 :
335 : template<MaybeAdding Adding>
336 : MOZ_ALWAYS_INLINE Entry& search(jsid id, const AutoKeepShapeTables&);
337 :
338 : template<MaybeAdding Adding>
339 : MOZ_ALWAYS_INLINE Entry& search(jsid id, const JS::AutoCheckCannotGC&);
340 :
341 : void trace(JSTracer* trc);
342 : #ifdef JSGC_HASH_TABLE_CHECKS
343 : void checkAfterMovingGC();
344 : #endif
345 :
346 : private:
347 521520 : Entry& getEntry(uint32_t i) const {
348 521520 : MOZ_ASSERT(i < capacity());
349 521520 : return entries_[i];
350 : }
351 381 : void decEntryCount() {
352 381 : MOZ_ASSERT(entryCount_ > 0);
353 381 : entryCount_--;
354 381 : }
355 2665 : void incEntryCount() {
356 2665 : entryCount_++;
357 2665 : MOZ_ASSERT(entryCount_ + removedCount_ <= capacity());
358 2665 : }
359 139 : void incRemovedCount() {
360 139 : removedCount_++;
361 139 : MOZ_ASSERT(entryCount_ + removedCount_ <= capacity());
362 139 : }
363 :
364 : /* By definition, hashShift = HASH_BITS - log2(capacity). */
365 532984 : uint32_t capacity() const { return JS_BIT(HASH_BITS - hashShift_); }
366 :
367 : /* Whether we need to grow. We want to do this if the load factor is >= 0.75 */
368 5436 : bool needsToGrow() const {
369 5436 : uint32_t size = capacity();
370 5436 : return entryCount_ + removedCount_ >= size - (size >> 2);
371 : }
372 :
373 : /*
374 : * Try to grow the table. On failure, reports out of memory on cx
375 : * and returns false. This will make any extant pointers into the
376 : * table invalid. Don't call this unless needsToGrow() is true.
377 : */
378 : bool grow(JSContext* cx);
379 : };
380 :
381 : // Ensures no shape tables are purged in the current zone.
382 : class MOZ_RAII AutoKeepShapeTables
383 : {
384 : JSContext* cx_;
385 : bool prev_;
386 :
387 : AutoKeepShapeTables(const AutoKeepShapeTables&) = delete;
388 : void operator=(const AutoKeepShapeTables&) = delete;
389 :
390 : public:
391 : explicit inline AutoKeepShapeTables(JSContext* cx);
392 : inline ~AutoKeepShapeTables();
393 : };
394 :
395 : /*
396 : * Use the reserved attribute bit to mean shadowability.
397 : */
398 : #define JSPROP_SHADOWABLE JSPROP_INTERNAL_USE_BIT
399 :
400 : /*
401 : * Shapes encode information about both a property lineage *and* a particular
402 : * property. This information is split across the Shape and the BaseShape
403 : * at shape->base(). Both Shape and BaseShape can be either owned or unowned
404 : * by, respectively, the Object or Shape referring to them.
405 : *
406 : * Owned Shapes are used in dictionary objects, and form a doubly linked list
407 : * whose entries are all owned by that dictionary. Unowned Shapes are all in
408 : * the property tree.
409 : *
410 : * Owned BaseShapes are used for shapes which have shape tables, including the
411 : * last properties in all dictionaries. Unowned BaseShapes compactly store
412 : * information common to many shapes. In a given zone there is a single
413 : * BaseShape for each combination of BaseShape information. This information is
414 : * cloned in owned BaseShapes so that information can be quickly looked up for a
415 : * given object or shape without regard to whether the base shape is owned or
416 : * not.
417 : *
418 : * All combinations of owned/unowned Shapes/BaseShapes are possible:
419 : *
420 : * Owned Shape, Owned BaseShape:
421 : *
422 : * Last property in a dictionary object. The BaseShape is transferred from
423 : * property to property as the object's last property changes.
424 : *
425 : * Owned Shape, Unowned BaseShape:
426 : *
427 : * Property in a dictionary object other than the last one.
428 : *
429 : * Unowned Shape, Owned BaseShape:
430 : *
431 : * Property in the property tree which has a shape table.
432 : *
433 : * Unowned Shape, Unowned BaseShape:
434 : *
435 : * Property in the property tree which does not have a shape table.
436 : *
437 : * BaseShapes additionally encode some information about the referring object
438 : * itself. This includes the object's class and various flags that may be set
439 : * for the object. Except for the class, this information is mutable and may
440 : * change when the object has an established property lineage. On such changes
441 : * the entire property lineage is not updated, but rather only the last property
442 : * (and its base shape). This works because only the object's last property is
443 : * used to query information about the object. Care must be taken to call
444 : * JSObject::canRemoveLastProperty when unwinding an object to an earlier
445 : * property, however.
446 : */
447 :
448 : class AccessorShape;
449 : class Shape;
450 : class UnownedBaseShape;
451 : struct StackBaseShape;
452 :
453 : class BaseShape : public gc::TenuredCell
454 : {
455 : public:
456 : friend class Shape;
457 : friend struct StackBaseShape;
458 : friend struct StackShape;
459 : friend void gc::MergeCompartments(JSCompartment* source, JSCompartment* target);
460 :
461 : enum Flag {
462 : /* Owned by the referring shape. */
463 : OWNED_SHAPE = 0x1,
464 :
465 : /* (0x2 and 0x4 are unused) */
466 :
467 : /*
468 : * Flags set which describe the referring object. Once set these cannot
469 : * be unset (except during object densification of sparse indexes), and
470 : * are transferred from shape to shape as the object's last property
471 : * changes.
472 : *
473 : * If you add a new flag here, please add appropriate code to
474 : * JSObject::dump to dump it as part of object representation.
475 : */
476 :
477 : DELEGATE = 0x8,
478 : NOT_EXTENSIBLE = 0x10,
479 : INDEXED = 0x20,
480 : HAS_INTERESTING_SYMBOL = 0x40,
481 : HAD_ELEMENTS_ACCESS = 0x80,
482 : WATCHED = 0x100,
483 : ITERATED_SINGLETON = 0x200,
484 : NEW_GROUP_UNKNOWN = 0x400,
485 : UNCACHEABLE_PROTO = 0x800,
486 : IMMUTABLE_PROTOTYPE = 0x1000,
487 :
488 : // See JSObject::isQualifiedVarObj().
489 : QUALIFIED_VAROBJ = 0x2000,
490 :
491 : // 0x4000 is unused.
492 :
493 : // For a function used as an interpreted constructor, whether a 'new'
494 : // type had constructor information cleared.
495 : NEW_SCRIPT_CLEARED = 0x8000,
496 :
497 : OBJECT_FLAG_MASK = 0xfff8
498 : };
499 :
500 : private:
501 : const Class* clasp_; /* Class of referring object. */
502 : uint32_t flags; /* Vector of above flags. */
503 : uint32_t slotSpan_; /* Object slot span for BaseShapes at
504 : * dictionary last properties. */
505 :
506 : /* For owned BaseShapes, the canonical unowned BaseShape. */
507 : GCPtrUnownedBaseShape unowned_;
508 :
509 : /* For owned BaseShapes, the shape's shape table. */
510 : ShapeTable* table_;
511 :
512 : #if JS_BITS_PER_WORD == 32
513 : // Ensure sizeof(BaseShape) is a multiple of gc::CellAlignBytes.
514 : uint32_t padding_;
515 : #endif
516 :
517 : BaseShape(const BaseShape& base) = delete;
518 : BaseShape& operator=(const BaseShape& other) = delete;
519 :
520 : public:
521 : void finalize(FreeOp* fop);
522 :
523 : explicit inline BaseShape(const StackBaseShape& base);
524 :
525 : /* Not defined: BaseShapes must not be stack allocated. */
526 : ~BaseShape();
527 :
528 0 : const Class* clasp() const { return clasp_; }
529 :
530 1432501 : bool isOwned() const { return !!(flags & OWNED_SHAPE); }
531 :
532 : static void copyFromUnowned(BaseShape& dest, UnownedBaseShape& src);
533 : inline void adoptUnowned(UnownedBaseShape* other);
534 :
535 2716 : void setOwned(UnownedBaseShape* unowned) {
536 2716 : flags |= OWNED_SHAPE;
537 2716 : unowned_ = unowned;
538 2716 : }
539 :
540 197911 : uint32_t getObjectFlags() const { return flags & OBJECT_FLAG_MASK; }
541 :
542 600679 : bool hasTable() const { MOZ_ASSERT_IF(table_, isOwned()); return table_ != nullptr; }
543 2716 : void setTable(ShapeTable* table) { MOZ_ASSERT(isOwned()); table_ = table; }
544 :
545 9677 : ShapeTable* maybeTable(const AutoKeepShapeTables&) const {
546 9677 : MOZ_ASSERT_IF(table_, isOwned());
547 9677 : return table_;
548 : }
549 751838 : ShapeTable* maybeTable(const JS::AutoCheckCannotGC&) const {
550 751838 : MOZ_ASSERT_IF(table_, isOwned());
551 751838 : return table_;
552 : }
553 0 : void maybePurgeTable() {
554 0 : if (table_ && table_->freeList() == SHAPE_INVALID_SLOT) {
555 0 : js_delete(table_);
556 0 : table_ = nullptr;
557 : }
558 0 : }
559 :
560 206885 : uint32_t slotSpan() const { MOZ_ASSERT(isOwned()); return slotSpan_; }
561 10235 : void setSlotSpan(uint32_t slotSpan) { MOZ_ASSERT(isOwned()); slotSpan_ = slotSpan; }
562 :
563 : /*
564 : * Lookup base shapes from the zone's baseShapes table, adding if not
565 : * already found.
566 : */
567 : static UnownedBaseShape* getUnowned(JSContext* cx, StackBaseShape& base);
568 :
569 : /* Get the canonical base shape. */
570 : inline UnownedBaseShape* unowned();
571 :
572 : /* Get the canonical base shape for an owned one. */
573 : inline UnownedBaseShape* baseUnowned();
574 :
575 : /* Get the canonical base shape for an unowned one (i.e. identity). */
576 : inline UnownedBaseShape* toUnowned();
577 :
578 : /* Check that an owned base shape is consistent with its unowned base. */
579 : void assertConsistency();
580 :
581 : /* For JIT usage */
582 : static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); }
583 :
584 : static const JS::TraceKind TraceKind = JS::TraceKind::BaseShape;
585 :
586 : void traceChildren(JSTracer* trc);
587 : void traceChildrenSkipShapeTable(JSTracer* trc);
588 :
589 : #ifdef DEBUG
590 : bool canSkipMarkingShapeTable(Shape* lastShape);
591 : #endif
592 :
593 : private:
594 : static void staticAsserts() {
595 : JS_STATIC_ASSERT(offsetof(BaseShape, clasp_) == offsetof(js::shadow::BaseShape, clasp_));
596 : static_assert(sizeof(BaseShape) % gc::CellAlignBytes == 0,
597 : "Things inheriting from gc::Cell must have a size that's "
598 : "a multiple of gc::CellAlignBytes");
599 : }
600 :
601 : void traceShapeTable(JSTracer* trc);
602 : };
603 :
604 : class UnownedBaseShape : public BaseShape {};
605 :
606 : UnownedBaseShape*
607 367831 : BaseShape::unowned()
608 : {
609 367831 : return isOwned() ? baseUnowned() : toUnowned();
610 : }
611 :
612 : UnownedBaseShape*
613 325863 : BaseShape::toUnowned()
614 : {
615 325863 : MOZ_ASSERT(!isOwned() && !unowned_);
616 325863 : return static_cast<UnownedBaseShape*>(this);
617 : }
618 :
619 : UnownedBaseShape*
620 66315 : BaseShape::baseUnowned()
621 : {
622 66315 : MOZ_ASSERT(isOwned() && unowned_);
623 66315 : return unowned_;
624 : }
625 :
626 : /* Entries for the per-zone baseShapes set of unowned base shapes. */
627 : struct StackBaseShape : public DefaultHasher<ReadBarriered<UnownedBaseShape*>>
628 : {
629 : uint32_t flags;
630 : const Class* clasp;
631 :
632 1064 : explicit StackBaseShape(BaseShape* base)
633 2128 : : flags(base->flags & BaseShape::OBJECT_FLAG_MASK),
634 2128 : clasp(base->clasp_)
635 1064 : {}
636 :
637 : inline StackBaseShape(JSContext* cx, const Class* clasp, uint32_t objectFlags);
638 : explicit inline StackBaseShape(Shape* shape);
639 :
640 : struct Lookup
641 : {
642 : uint32_t flags;
643 : const Class* clasp;
644 :
645 17134 : MOZ_IMPLICIT Lookup(const StackBaseShape& base)
646 17134 : : flags(base.flags), clasp(base.clasp)
647 17134 : {}
648 :
649 0 : MOZ_IMPLICIT Lookup(UnownedBaseShape* base)
650 0 : : flags(base->getObjectFlags()), clasp(base->clasp())
651 : {
652 0 : MOZ_ASSERT(!base->isOwned());
653 0 : }
654 :
655 : explicit Lookup(const ReadBarriered<UnownedBaseShape*>& base)
656 : : flags(base.unbarrieredGet()->getObjectFlags()), clasp(base.unbarrieredGet()->clasp())
657 : {
658 : MOZ_ASSERT(!base.unbarrieredGet()->isOwned());
659 : }
660 : };
661 :
662 17135 : static HashNumber hash(const Lookup& lookup) {
663 17135 : HashNumber hash = lookup.flags;
664 17135 : hash = mozilla::RotateLeft(hash, 4) ^ (uintptr_t(lookup.clasp) >> 3);
665 17135 : return hash;
666 : }
667 13117 : static inline bool match(const ReadBarriered<UnownedBaseShape*>& key, const Lookup& lookup) {
668 26216 : return key.unbarrieredGet()->flags == lookup.flags &&
669 26216 : key.unbarrieredGet()->clasp_ == lookup.clasp;
670 : }
671 : };
672 :
673 : static MOZ_ALWAYS_INLINE js::HashNumber
674 365741 : HashId(jsid id)
675 : {
676 : // HashGeneric alone would work, but bits of atom and symbol addresses
677 : // could then be recovered from the hash code. See bug 1330769.
678 365741 : if (MOZ_LIKELY(JSID_IS_ATOM(id)))
679 356781 : return JSID_TO_ATOM(id)->hash();
680 8961 : if (JSID_IS_SYMBOL(id))
681 8776 : return JSID_TO_SYMBOL(id)->hash();
682 185 : return mozilla::HashGeneric(JSID_BITS(id));
683 : }
684 :
685 : template <>
686 : struct DefaultHasher<jsid>
687 : {
688 : typedef jsid Lookup;
689 3981 : static HashNumber hash(jsid id) {
690 3981 : return HashId(id);
691 : }
692 21 : static bool match(jsid id1, jsid id2) {
693 21 : return id1 == id2;
694 : }
695 : };
696 :
697 : using BaseShapeSet = JS::WeakCache<JS::GCHashSet<ReadBarriered<UnownedBaseShape*>,
698 : StackBaseShape,
699 : SystemAllocPolicy>>;
700 :
701 : class Shape : public gc::TenuredCell
702 : {
703 : friend class ::JSObject;
704 : friend class ::JSFunction;
705 : friend class NativeObject;
706 : friend class PropertyTree;
707 : friend class TenuringTracer;
708 : friend struct StackBaseShape;
709 : friend struct StackShape;
710 : friend class JS::ubi::Concrete<Shape>;
711 : friend class js::gc::RelocationOverlay;
712 :
713 : protected:
714 : GCPtrBaseShape base_;
715 : PreBarrieredId propid_;
716 :
717 : enum SlotInfo : uint32_t
718 : {
719 : /* Number of fixed slots in objects with this shape. */
720 : // FIXED_SLOTS_MAX is the biggest count of fixed slots a Shape can store
721 : FIXED_SLOTS_MAX = 0x1f,
722 : FIXED_SLOTS_SHIFT = 27,
723 : FIXED_SLOTS_MASK = uint32_t(FIXED_SLOTS_MAX << FIXED_SLOTS_SHIFT),
724 :
725 : /*
726 : * numLinearSearches starts at zero and is incremented initially on
727 : * search() calls. Once numLinearSearches reaches LINEAR_SEARCHES_MAX,
728 : * the table is created on the next search() call. The table can also
729 : * be created when hashifying for dictionary mode.
730 : */
731 : LINEAR_SEARCHES_MAX = 0x7,
732 : LINEAR_SEARCHES_SHIFT = 24,
733 : LINEAR_SEARCHES_MASK = LINEAR_SEARCHES_MAX << LINEAR_SEARCHES_SHIFT,
734 :
735 : /*
736 : * Mask to get the index in object slots for shapes which hasSlot().
737 : * For !hasSlot() shapes in the property tree with a parent, stores the
738 : * parent's slot index (which may be invalid), and invalid for all
739 : * other shapes.
740 : */
741 : SLOT_MASK = JS_BIT(24) - 1
742 : };
743 :
744 : uint32_t slotInfo; /* mask of above info */
745 : uint8_t attrs; /* attributes, see jsapi.h JSPROP_* */
746 : uint8_t flags; /* flags, see below for defines */
747 :
748 : GCPtrShape parent; /* parent node, reverse for..in order */
749 : /* kids is valid when !inDictionary(), listp is valid when inDictionary(). */
750 : union {
751 : KidsPointer kids; /* null, single child, or a tagged ptr
752 : to many-kids data structure */
753 : GCPtrShape* listp; /* dictionary list starting at shape_
754 : has a double-indirect back pointer,
755 : either to the next shape's parent if not
756 : last, else to obj->shape_ */
757 : };
758 :
759 : template<MaybeAdding Adding = MaybeAdding::NotAdding>
760 : static MOZ_ALWAYS_INLINE Shape* search(JSContext* cx, Shape* start, jsid id);
761 :
762 : template<MaybeAdding Adding = MaybeAdding::NotAdding>
763 : static inline MOZ_MUST_USE bool search(JSContext* cx, Shape* start, jsid id,
764 : const AutoKeepShapeTables&,
765 : Shape** pshape, ShapeTable::Entry** pentry);
766 :
767 : static inline Shape* searchNoHashify(Shape* start, jsid id);
768 :
769 : void removeFromDictionary(NativeObject* obj);
770 : void insertIntoDictionary(GCPtrShape* dictp);
771 :
772 : inline void initDictionaryShape(const StackShape& child, uint32_t nfixed,
773 : GCPtrShape* dictp);
774 :
775 : /* Replace the base shape of the last shape in a non-dictionary lineage with base. */
776 : static Shape* replaceLastProperty(JSContext* cx, StackBaseShape& base,
777 : TaggedProto proto, HandleShape shape);
778 :
779 : /*
780 : * This function is thread safe if every shape in the lineage of |shape|
781 : * is thread local, which is the case when we clone the entire shape
782 : * lineage in preparation for converting an object to dictionary mode.
783 : */
784 : static bool hashify(JSContext* cx, Shape* shape);
785 : void handoffTableTo(Shape* newShape);
786 :
787 94909 : void setParent(Shape* p) {
788 94909 : MOZ_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(),
789 : p->maybeSlot() <= maybeSlot());
790 94909 : MOZ_ASSERT_IF(p && !inDictionary(),
791 : hasSlot() == (p->maybeSlot() != maybeSlot()));
792 94909 : parent = p;
793 94909 : }
794 :
795 2716 : bool ensureOwnBaseShape(JSContext* cx) {
796 2716 : if (base()->isOwned())
797 0 : return true;
798 2716 : return makeOwnBaseShape(cx);
799 : }
800 :
801 : bool makeOwnBaseShape(JSContext* cx);
802 :
803 : MOZ_ALWAYS_INLINE MOZ_MUST_USE bool maybeCreateTableForLookup(JSContext* cx);
804 :
805 : public:
806 600680 : bool hasTable() const { return base()->hasTable(); }
807 :
808 9677 : ShapeTable* maybeTable(const AutoKeepShapeTables& keep) const {
809 9677 : return base()->maybeTable(keep);
810 : }
811 751428 : ShapeTable* maybeTable(const JS::AutoCheckCannotGC& check) const {
812 751428 : return base()->maybeTable(check);
813 : }
814 :
815 : template <typename T>
816 11134 : MOZ_MUST_USE ShapeTable* ensureTableForDictionary(JSContext* cx, const T& nogc) {
817 11134 : MOZ_ASSERT(inDictionary());
818 11134 : if (ShapeTable* table = maybeTable(nogc))
819 11134 : return table;
820 0 : if (!hashify(cx, this))
821 0 : return nullptr;
822 0 : ShapeTable* table = maybeTable(nogc);
823 0 : MOZ_ASSERT(table);
824 0 : return table;
825 : }
826 :
827 0 : void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
828 : JS::ShapeInfo* info) const
829 : {
830 0 : JS::AutoCheckCannotGC nogc;
831 0 : if (ShapeTable* table = maybeTable(nogc)) {
832 0 : if (inDictionary())
833 0 : info->shapesMallocHeapDictTables += table->sizeOfIncludingThis(mallocSizeOf);
834 : else
835 0 : info->shapesMallocHeapTreeTables += table->sizeOfIncludingThis(mallocSizeOf);
836 : }
837 :
838 0 : if (!inDictionary() && kids.isHash())
839 0 : info->shapesMallocHeapTreeKids += kids.toHash()->sizeOfIncludingThis(mallocSizeOf);
840 0 : }
841 :
842 774848 : bool isAccessorShape() const {
843 774848 : MOZ_ASSERT_IF(flags & ACCESSOR_SHAPE, getAllocKind() == gc::AllocKind::ACCESSOR_SHAPE);
844 774848 : return flags & ACCESSOR_SHAPE;
845 : }
846 131302 : AccessorShape& asAccessorShape() const {
847 131302 : MOZ_ASSERT(isAccessorShape());
848 131302 : return *(AccessorShape*)this;
849 : }
850 :
851 30229 : const GCPtrShape& previous() const { return parent; }
852 :
853 : template <AllowGC allowGC>
854 : class Range {
855 : protected:
856 : friend class Shape;
857 :
858 : typename MaybeRooted<Shape*, allowGC>::RootType cursor;
859 :
860 : public:
861 : Range(JSContext* cx, Shape* shape) : cursor(cx, shape) {
862 : JS_STATIC_ASSERT(allowGC == CanGC);
863 : }
864 :
865 266451 : explicit Range(Shape* shape) : cursor((JSContext*) nullptr, shape) {
866 : JS_STATIC_ASSERT(allowGC == NoGC);
867 266451 : }
868 :
869 5194801 : bool empty() const {
870 5194801 : return !cursor || cursor->isEmptyShape();
871 : }
872 :
873 90695 : Shape& front() const {
874 90695 : MOZ_ASSERT(!empty());
875 90695 : return *cursor;
876 : }
877 :
878 2418924 : void popFront() {
879 2418924 : MOZ_ASSERT(!empty());
880 2418896 : cursor = cursor->parent;
881 2418900 : }
882 : };
883 :
884 3418562 : const Class* getObjectClass() const {
885 3418562 : return base()->clasp_;
886 : }
887 :
888 : static Shape* setObjectFlags(JSContext* cx,
889 : BaseShape::Flag flag, TaggedProto proto, Shape* last);
890 :
891 182649 : uint32_t getObjectFlags() const { return base()->getObjectFlags(); }
892 687424 : bool hasAllObjectFlags(BaseShape::Flag flags) const {
893 687424 : MOZ_ASSERT(flags);
894 687424 : MOZ_ASSERT(!(flags & ~BaseShape::OBJECT_FLAG_MASK));
895 687424 : return (base()->flags & flags) == flags;
896 : }
897 :
898 : protected:
899 : /*
900 : * Implementation-private bits stored in shape->flags. See public: enum {}
901 : * flags further below, which were allocated FCFS over time, so interleave
902 : * with these bits.
903 : */
904 : enum {
905 : /* Property stored in per-object dictionary, not shared property tree. */
906 : IN_DICTIONARY = 0x01,
907 :
908 : /*
909 : * Slotful property was stored to more than once. This is used as a
910 : * hint for type inference.
911 : */
912 : OVERWRITTEN = 0x02,
913 :
914 : /*
915 : * This shape is an AccessorShape, a fat Shape that can store
916 : * getter/setter information.
917 : */
918 : ACCESSOR_SHAPE = 0x04,
919 :
920 : /* Flags used to speed up isBigEnoughForAShapeTable(). */
921 : HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE = 0x08,
922 : CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE = 0x10,
923 : };
924 :
925 : /* Get a shape identical to this one, without parent/kids information. */
926 : inline Shape(const StackShape& other, uint32_t nfixed);
927 :
928 : /* Used by EmptyShape (see jsscopeinlines.h). */
929 : inline Shape(UnownedBaseShape* base, uint32_t nfixed);
930 :
931 : /* Copy constructor disabled, to avoid misuse of the above form. */
932 : Shape(const Shape& other) = delete;
933 :
934 : /* Allocate a new shape based on the given StackShape. */
935 : static inline Shape* new_(JSContext* cx, Handle<StackShape> other, uint32_t nfixed);
936 :
937 : /*
938 : * Whether this shape has a valid slot value. This may be true even if
939 : * !hasSlot() (see SlotInfo comment above), and may be false even if
940 : * hasSlot() if the shape is being constructed and has not had a slot
941 : * assigned yet. After construction, hasSlot() implies !hasMissingSlot().
942 : */
943 3577894 : bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
944 :
945 : public:
946 6938430 : bool inDictionary() const {
947 6938430 : return (flags & IN_DICTIONARY) != 0;
948 : }
949 :
950 : inline GetterOp getter() const;
951 253104 : bool hasDefaultGetter() const { return !getter(); }
952 732 : GetterOp getterOp() const { MOZ_ASSERT(!hasGetterValue()); return getter(); }
953 : inline JSObject* getterObject() const;
954 24319 : bool hasGetterObject() const { return hasGetterValue() && getterObject(); }
955 :
956 : // Per ES5, decode null getterObj as the undefined value, which encodes as null.
957 7824 : Value getterValue() const {
958 7824 : MOZ_ASSERT(hasGetterValue());
959 7824 : if (JSObject* getterObj = getterObject())
960 7824 : return ObjectValue(*getterObj);
961 0 : return UndefinedValue();
962 : }
963 :
964 54 : Value getterOrUndefined() const {
965 54 : return hasGetterValue() ? getterValue() : UndefinedValue();
966 : }
967 :
968 : inline SetterOp setter() const;
969 116499 : bool hasDefaultSetter() const { return !setter(); }
970 72 : SetterOp setterOp() const { MOZ_ASSERT(!hasSetterValue()); return setter(); }
971 : inline JSObject* setterObject() const;
972 21223 : bool hasSetterObject() const { return hasSetterValue() && setterObject(); }
973 :
974 : // Per ES5, decode null setterObj as the undefined value, which encodes as null.
975 10 : Value setterValue() const {
976 10 : MOZ_ASSERT(hasSetterValue());
977 10 : if (JSObject* setterObj = setterObject())
978 10 : return ObjectValue(*setterObj);
979 0 : return UndefinedValue();
980 : }
981 :
982 0 : Value setterOrUndefined() const {
983 0 : return hasSetterValue() ? setterValue() : UndefinedValue();
984 : }
985 :
986 2309 : void setOverwritten() {
987 2309 : flags |= OVERWRITTEN;
988 2309 : }
989 960 : bool hadOverwrite() const {
990 960 : return flags & OVERWRITTEN;
991 : }
992 :
993 : void update(GetterOp getter, SetterOp setter, uint8_t attrs);
994 :
995 2095 : bool matches(const Shape* other) const {
996 3150 : return propid_.get() == other->propid_.get() &&
997 1055 : matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs, other->flags,
998 2095 : other->getter(), other->setter());
999 : }
1000 :
1001 : inline bool matches(const StackShape& other) const;
1002 :
1003 88030 : bool matchesParamsAfterId(BaseShape* base, uint32_t aslot, unsigned aattrs, unsigned aflags,
1004 : GetterOp rawGetter, SetterOp rawSetter) const
1005 : {
1006 173899 : return base->unowned() == this->base()->unowned() &&
1007 171551 : maybeSlot() == aslot &&
1008 170929 : attrs == aattrs &&
1009 258269 : getter() == rawGetter &&
1010 173022 : setter() == rawSetter;
1011 : }
1012 :
1013 6170879 : BaseShape* base() const { return base_.get(); }
1014 :
1015 755954 : bool hasSlot() const {
1016 755954 : return (attrs & JSPROP_SHARED) == 0;
1017 : }
1018 292938 : uint32_t slot() const { MOZ_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); }
1019 6427674 : uint32_t maybeSlot() const {
1020 6427674 : return slotInfo & SLOT_MASK;
1021 : }
1022 :
1023 6097016 : bool isEmptyShape() const {
1024 6097016 : MOZ_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
1025 6097021 : return JSID_IS_EMPTY(propid_);
1026 : }
1027 :
1028 2935113 : uint32_t slotSpan(const Class* clasp) const {
1029 2935113 : MOZ_ASSERT(!inDictionary());
1030 : // Proxy classes have reserved slots, but proxies manage their own slot
1031 : // layout. This means all non-native object shapes have nfixed == 0 and
1032 : // slotSpan == 0.
1033 2935145 : uint32_t free = clasp->isProxy() ? 0 : JSSLOT_FREE(clasp);
1034 2935151 : return hasMissingSlot() ? free : Max(free, maybeSlot() + 1);
1035 : }
1036 :
1037 2934894 : uint32_t slotSpan() const {
1038 2934894 : return slotSpan(getObjectClass());
1039 : }
1040 :
1041 924 : void setSlot(uint32_t slot) {
1042 924 : MOZ_ASSERT(slot <= SHAPE_INVALID_SLOT);
1043 924 : slotInfo = slotInfo & ~Shape::SLOT_MASK;
1044 924 : slotInfo = slotInfo | slot;
1045 924 : }
1046 :
1047 679078 : uint32_t numFixedSlots() const {
1048 679078 : return slotInfo >> FIXED_SLOTS_SHIFT;
1049 : }
1050 :
1051 0 : void setNumFixedSlots(uint32_t nfixed) {
1052 0 : MOZ_ASSERT(nfixed < FIXED_SLOTS_MAX);
1053 0 : slotInfo = slotInfo & ~FIXED_SLOTS_MASK;
1054 0 : slotInfo = slotInfo | (nfixed << FIXED_SLOTS_SHIFT);
1055 0 : }
1056 :
1057 374060 : uint32_t numLinearSearches() const {
1058 374060 : return (slotInfo & LINEAR_SEARCHES_MASK) >> LINEAR_SEARCHES_SHIFT;
1059 : }
1060 :
1061 109451 : void incrementNumLinearSearches() {
1062 109451 : uint32_t count = numLinearSearches();
1063 109451 : MOZ_ASSERT(count < LINEAR_SEARCHES_MAX);
1064 109451 : slotInfo = slotInfo & ~LINEAR_SEARCHES_MASK;
1065 109451 : slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT);
1066 109451 : }
1067 :
1068 784448 : const PreBarrieredId& propid() const {
1069 784448 : MOZ_ASSERT(!isEmptyShape());
1070 784436 : MOZ_ASSERT(!JSID_IS_VOID(propid_));
1071 784425 : return propid_;
1072 : }
1073 6666544 : PreBarrieredId& propidRef() { MOZ_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
1074 321370 : jsid propidRaw() const {
1075 : // Return the actual jsid, not an internal reference.
1076 321370 : return propid();
1077 : }
1078 :
1079 13332 : uint8_t attributes() const { return attrs; }
1080 18311 : bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; }
1081 12617 : bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; }
1082 142361 : bool writable() const {
1083 142361 : return (attrs & JSPROP_READONLY) == 0;
1084 : }
1085 90937 : bool hasGetterValue() const { return attrs & JSPROP_GETTER; }
1086 46999 : bool hasSetterValue() const { return attrs & JSPROP_SETTER; }
1087 :
1088 42502 : bool isDataDescriptor() const {
1089 42502 : return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
1090 : }
1091 449 : bool isAccessorDescriptor() const {
1092 449 : return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
1093 : }
1094 :
1095 0 : bool hasShadowable() const { return attrs & JSPROP_SHADOWABLE; }
1096 :
1097 117048 : uint32_t entryCount() {
1098 234097 : JS::AutoCheckCannotGC nogc;
1099 117049 : if (ShapeTable* table = maybeTable(nogc))
1100 17628 : return table->entryCount();
1101 99421 : uint32_t count = 0;
1102 2021724 : for (Shape::Range<NoGC> r(this); !r.empty(); r.popFront())
1103 1922303 : ++count;
1104 99421 : return count;
1105 : }
1106 :
1107 : private:
1108 155170 : bool isBigEnoughForAShapeTableSlow() {
1109 155170 : uint32_t count = 0;
1110 560663 : for (Shape::Range<NoGC> r(this); !r.empty(); r.popFront()) {
1111 407262 : ++count;
1112 407262 : if (count >= ShapeTable::MIN_ENTRIES)
1113 1769 : return true;
1114 : }
1115 153401 : return false;
1116 : }
1117 5311 : void clearCachedBigEnoughForShapeTable() {
1118 5311 : flags &= ~(HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE | CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE);
1119 5311 : }
1120 :
1121 : public:
1122 155170 : bool isBigEnoughForAShapeTable() {
1123 155170 : MOZ_ASSERT(!hasTable());
1124 :
1125 : // isBigEnoughForAShapeTableSlow is pretty inefficient so we only call
1126 : // it once and cache the result.
1127 :
1128 155170 : if (flags & HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE) {
1129 150809 : bool res = flags & CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
1130 150809 : MOZ_ASSERT(res == isBigEnoughForAShapeTableSlow());
1131 150809 : return res;
1132 : }
1133 :
1134 4361 : MOZ_ASSERT(!(flags & CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE));
1135 :
1136 4361 : bool res = isBigEnoughForAShapeTableSlow();
1137 4361 : if (res)
1138 1746 : flags |= CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
1139 4361 : flags |= HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
1140 4361 : return res;
1141 : }
1142 :
1143 : #ifdef DEBUG
1144 : void dump(FILE* fp) const;
1145 : void dumpSubtree(int level, FILE* fp) const;
1146 : #endif
1147 :
1148 : void sweep();
1149 : void finalize(FreeOp* fop);
1150 : void removeChild(Shape* child);
1151 :
1152 : static const JS::TraceKind TraceKind = JS::TraceKind::Shape;
1153 :
1154 : void traceChildren(JSTracer* trc);
1155 :
1156 : MOZ_ALWAYS_INLINE Shape* search(JSContext* cx, jsid id);
1157 : MOZ_ALWAYS_INLINE Shape* searchLinear(jsid id);
1158 :
1159 : void fixupAfterMovingGC();
1160 : void fixupGetterSetterForBarrier(JSTracer* trc);
1161 : void updateBaseShapeAfterMovingGC();
1162 :
1163 : /* For JIT usage */
1164 : static inline size_t offsetOfBase() { return offsetof(Shape, base_); }
1165 0 : static inline size_t offsetOfSlotInfo() { return offsetof(Shape, slotInfo); }
1166 0 : static inline uint32_t fixedSlotsMask() { return FIXED_SLOTS_MASK; }
1167 :
1168 : private:
1169 : void fixupDictionaryShapeAfterMovingGC();
1170 : void fixupShapeTreeAfterMovingGC();
1171 :
1172 : static void staticAsserts() {
1173 : JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base));
1174 : JS_STATIC_ASSERT(offsetof(Shape, slotInfo) == offsetof(js::shadow::Shape, slotInfo));
1175 : JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Shape::FIXED_SLOTS_SHIFT);
1176 : }
1177 : };
1178 :
1179 : /* Fat Shape used for accessor properties. */
1180 : class AccessorShape : public Shape
1181 : {
1182 : friend class Shape;
1183 : friend class NativeObject;
1184 :
1185 : union {
1186 : GetterOp rawGetter; /* getter hook for shape */
1187 : JSObject* getterObj; /* user-defined callable "get" object or
1188 : null if shape->hasGetterValue() */
1189 : };
1190 : union {
1191 : SetterOp rawSetter; /* setter hook for shape */
1192 : JSObject* setterObj; /* user-defined callable "set" object or
1193 : null if shape->hasSetterValue() */
1194 : };
1195 :
1196 : public:
1197 : /* Get a shape identical to this one, without parent/kids information. */
1198 : inline AccessorShape(const StackShape& other, uint32_t nfixed);
1199 : };
1200 :
1201 : inline
1202 12781 : StackBaseShape::StackBaseShape(Shape* shape)
1203 12781 : : flags(shape->getObjectFlags()),
1204 25562 : clasp(shape->getObjectClass())
1205 12781 : {}
1206 :
1207 139630 : class MOZ_RAII AutoRooterGetterSetter
1208 : {
1209 19963 : class Inner final : private JS::CustomAutoRooter
1210 : {
1211 : public:
1212 : inline Inner(JSContext* cx, uint8_t attrs, GetterOp* pgetter_, SetterOp* psetter_);
1213 :
1214 : private:
1215 : virtual void trace(JSTracer* trc);
1216 :
1217 : uint8_t attrs;
1218 : GetterOp* pgetter;
1219 : SetterOp* psetter;
1220 : };
1221 :
1222 : public:
1223 : inline AutoRooterGetterSetter(JSContext* cx, uint8_t attrs,
1224 : GetterOp* pgetter, SetterOp* psetter
1225 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
1226 : inline AutoRooterGetterSetter(JSContext* cx, uint8_t attrs,
1227 : JSNative* pgetter, JSNative* psetter
1228 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
1229 :
1230 : private:
1231 : mozilla::Maybe<Inner> inner;
1232 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
1233 : };
1234 :
1235 : struct EmptyShape : public js::Shape
1236 : {
1237 5252 : EmptyShape(UnownedBaseShape* base, uint32_t nfixed)
1238 5252 : : js::Shape(base, nfixed)
1239 5252 : { }
1240 :
1241 : static Shape* new_(JSContext* cx, Handle<UnownedBaseShape*> base, uint32_t nfixed);
1242 :
1243 : /*
1244 : * Lookup an initial shape matching the given parameters, creating an empty
1245 : * shape if none was found.
1246 : */
1247 : static Shape* getInitialShape(JSContext* cx, const Class* clasp,
1248 : TaggedProto proto, size_t nfixed, uint32_t objectFlags = 0);
1249 : static Shape* getInitialShape(JSContext* cx, const Class* clasp,
1250 : TaggedProto proto, gc::AllocKind kind, uint32_t objectFlags = 0);
1251 :
1252 : /*
1253 : * Reinsert an alternate initial shape, to be returned by future
1254 : * getInitialShape calls, until the new shape becomes unreachable in a GC
1255 : * and the table entry is purged.
1256 : */
1257 : static void insertInitialShape(JSContext* cx, HandleShape shape, HandleObject proto);
1258 :
1259 : /*
1260 : * Some object subclasses are allocated with a built-in set of properties.
1261 : * The first time such an object is created, these built-in properties must
1262 : * be set manually, to compute an initial shape. Afterward, that initial
1263 : * shape can be reused for newly-created objects that use the subclass's
1264 : * standard prototype. This method should be used in a post-allocation
1265 : * init method, to ensure that objects of such subclasses compute and cache
1266 : * the initial shape, if it hasn't already been computed.
1267 : */
1268 : template<class ObjectSubclass>
1269 : static inline bool
1270 : ensureInitialCustomShape(JSContext* cx, Handle<ObjectSubclass*> obj);
1271 : };
1272 :
1273 : // InitialShapeProto stores either:
1274 : //
1275 : // * A TaggedProto (or ReadBarriered<TaggedProto>).
1276 : //
1277 : // * A JSProtoKey. This is used instead of the TaggedProto if the proto is one
1278 : // of the global's builtin prototypes. For instance, if the proto is the
1279 : // initial Object.prototype, we use key_ = JSProto_Object, proto_ = nullptr.
1280 : //
1281 : // Using the JSProtoKey here is an optimization that lets us share more shapes
1282 : // across compartments within a zone.
1283 : template <typename PtrType>
1284 230000 : class InitialShapeProto
1285 : {
1286 : template <typename T> friend class InitialShapeProto;
1287 :
1288 : JSProtoKey key_;
1289 : PtrType proto_;
1290 :
1291 : public:
1292 : InitialShapeProto()
1293 : : key_(JSProto_LIMIT), proto_()
1294 : {}
1295 :
1296 0 : InitialShapeProto(JSProtoKey key, TaggedProto proto)
1297 0 : : key_(key), proto_(proto)
1298 0 : {}
1299 :
1300 : template <typename T>
1301 11215 : explicit InitialShapeProto(const InitialShapeProto<T>& other)
1302 11215 : : key_(other.key()), proto_(other.proto_)
1303 11215 : {}
1304 :
1305 152630 : explicit InitialShapeProto(TaggedProto proto)
1306 152630 : : key_(JSProto_LIMIT), proto_(proto)
1307 152630 : {}
1308 6876 : explicit InitialShapeProto(JSProtoKey key)
1309 6876 : : key_(key), proto_(nullptr)
1310 : {
1311 6876 : MOZ_ASSERT(key < JSProto_LIMIT);
1312 6876 : }
1313 :
1314 445027 : JSProtoKey key() const {
1315 445027 : return key_;
1316 : }
1317 433647 : const PtrType& proto() const {
1318 433647 : return proto_;
1319 : }
1320 0 : void setProto(TaggedProto proto) {
1321 0 : proto_ = proto;
1322 0 : }
1323 :
1324 0 : bool operator==(const InitialShapeProto& other) const {
1325 0 : return key_ == other.key_ && proto_ == other.proto_;
1326 : }
1327 : };
1328 :
1329 : template <>
1330 : struct MovableCellHasher<InitialShapeProto<ReadBarriered<TaggedProto>>>
1331 : {
1332 : using Key = InitialShapeProto<ReadBarriered<TaggedProto>>;
1333 : using Lookup = InitialShapeProto<TaggedProto>;
1334 :
1335 : static bool hasHash(const Lookup& l) {
1336 : return MovableCellHasher<TaggedProto>::hasHash(l.proto());
1337 : }
1338 : static bool ensureHash(const Lookup& l) {
1339 : return MovableCellHasher<TaggedProto>::ensureHash(l.proto());
1340 : }
1341 159507 : static HashNumber hash(const Lookup& l) {
1342 159507 : return HashNumber(l.key()) ^ MovableCellHasher<TaggedProto>::hash(l.proto());
1343 : }
1344 137163 : static bool match(const Key& k, const Lookup& l) {
1345 274239 : return k.key() == l.key() &&
1346 274239 : MovableCellHasher<TaggedProto>::match(k.proto().unbarrieredGet(), l.proto());
1347 : }
1348 : };
1349 :
1350 : /*
1351 : * Entries for the per-zone initialShapes set indexing initial shapes for
1352 : * objects in the zone and the associated types.
1353 : */
1354 59279 : struct InitialShapeEntry
1355 : {
1356 : /*
1357 : * Initial shape to give to the object. This is an empty shape, except for
1358 : * certain classes (e.g. String, RegExp) which may add certain baked-in
1359 : * properties.
1360 : */
1361 : ReadBarriered<Shape*> shape;
1362 :
1363 : /*
1364 : * Matching prototype for the entry. The shape of an object determines its
1365 : * prototype, but the prototype cannot be determined from the shape itself.
1366 : */
1367 : using ShapeProto = InitialShapeProto<ReadBarriered<TaggedProto>>;
1368 : ShapeProto proto;
1369 :
1370 : /* State used to determine a match on an initial shape. */
1371 : struct Lookup {
1372 : using ShapeProto = InitialShapeProto<TaggedProto>;
1373 : const Class* clasp;
1374 : ShapeProto proto;
1375 : uint32_t nfixed;
1376 : uint32_t baseFlags;
1377 :
1378 159507 : Lookup(const Class* clasp, ShapeProto proto, uint32_t nfixed, uint32_t baseFlags)
1379 159507 : : clasp(clasp), proto(proto), nfixed(nfixed), baseFlags(baseFlags)
1380 159506 : {}
1381 :
1382 : explicit Lookup(const InitialShapeEntry& entry)
1383 : : proto(entry.proto.key(),
1384 : entry.proto.proto().unbarrieredGet())
1385 : {
1386 : const Shape* shape = entry.shape.unbarrieredGet();
1387 : clasp = shape->getObjectClass();
1388 : nfixed = shape->numFixedSlots();
1389 : baseFlags = shape->getObjectFlags();
1390 : }
1391 : };
1392 :
1393 : inline InitialShapeEntry();
1394 : inline InitialShapeEntry(Shape* shape, const Lookup::ShapeProto& proto);
1395 :
1396 159508 : static HashNumber hash(const Lookup& lookup) {
1397 319012 : return (mozilla::RotateLeft(uintptr_t(lookup.clasp) >> 3, 4) ^
1398 159507 : MovableCellHasher<ShapeProto>::hash(lookup.proto)) +
1399 159504 : lookup.nfixed;
1400 : }
1401 159981 : static inline bool match(const InitialShapeEntry& key, const Lookup& lookup) {
1402 159981 : const Shape* shape = key.shape.unbarrieredGet();
1403 159981 : return lookup.clasp == shape->getObjectClass()
1404 159032 : && lookup.nfixed == shape->numFixedSlots()
1405 156460 : && lookup.baseFlags == shape->getObjectFlags()
1406 297144 : && MovableCellHasher<ShapeProto>::match(key.proto, lookup.proto);
1407 : }
1408 0 : static void rekey(InitialShapeEntry& k, const InitialShapeEntry& newKey) {
1409 0 : k = newKey;
1410 0 : }
1411 :
1412 0 : bool needsSweep() {
1413 0 : Shape* ushape = shape.unbarrieredGet();
1414 0 : TaggedProto uproto = proto.proto().unbarrieredGet();
1415 0 : JSObject* protoObj = uproto.raw();
1416 0 : return (gc::IsAboutToBeFinalizedUnbarriered(&ushape) ||
1417 0 : (uproto.isObject() && gc::IsAboutToBeFinalizedUnbarriered(&protoObj)));
1418 : }
1419 :
1420 0 : bool operator==(const InitialShapeEntry& other) const {
1421 0 : return shape == other.shape && proto == other.proto;
1422 : }
1423 : };
1424 :
1425 : using InitialShapeSet = JS::WeakCache<JS::GCHashSet<InitialShapeEntry,
1426 : InitialShapeEntry,
1427 : SystemAllocPolicy>>;
1428 :
1429 : struct StackShape
1430 : {
1431 : /* For performance, StackShape only roots when absolutely necessary. */
1432 : UnownedBaseShape* base;
1433 : jsid propid;
1434 : GetterOp rawGetter;
1435 : SetterOp rawSetter;
1436 : uint32_t slot_;
1437 : uint8_t attrs;
1438 : uint8_t flags;
1439 :
1440 146543 : explicit StackShape(UnownedBaseShape* base, jsid propid, uint32_t slot,
1441 : unsigned attrs, unsigned flags)
1442 146543 : : base(base),
1443 : propid(propid),
1444 : rawGetter(nullptr),
1445 : rawSetter(nullptr),
1446 : slot_(slot),
1447 : attrs(uint8_t(attrs)),
1448 146543 : flags(uint8_t(flags))
1449 : {
1450 146543 : MOZ_ASSERT(base);
1451 146543 : MOZ_ASSERT(!JSID_IS_VOID(propid));
1452 146542 : MOZ_ASSERT(slot <= SHAPE_INVALID_SLOT);
1453 146542 : MOZ_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
1454 146542 : }
1455 :
1456 38850 : explicit StackShape(Shape* shape)
1457 38850 : : base(shape->base()->unowned()),
1458 38850 : propid(shape->propidRef()),
1459 38850 : rawGetter(shape->getter()),
1460 38849 : rawSetter(shape->setter()),
1461 38850 : slot_(shape->maybeSlot()),
1462 38850 : attrs(shape->attrs),
1463 233099 : flags(shape->flags)
1464 38850 : {}
1465 :
1466 117460 : void updateGetterSetter(GetterOp rawGetter, SetterOp rawSetter) {
1467 117460 : if (rawGetter || rawSetter || (attrs & (JSPROP_GETTER|JSPROP_SETTER)))
1468 16530 : flags |= Shape::ACCESSOR_SHAPE;
1469 : else
1470 100930 : flags &= ~Shape::ACCESSOR_SHAPE;
1471 :
1472 117460 : this->rawGetter = rawGetter;
1473 117460 : this->rawSetter = rawSetter;
1474 117460 : }
1475 :
1476 122090 : bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
1477 104275 : bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
1478 :
1479 1965 : uint32_t slot() const { MOZ_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; }
1480 200127 : uint32_t maybeSlot() const { return slot_; }
1481 :
1482 117332 : void setSlot(uint32_t slot) {
1483 117332 : MOZ_ASSERT(slot <= SHAPE_INVALID_SLOT);
1484 117332 : slot_ = slot;
1485 117332 : }
1486 :
1487 343001 : bool isAccessorShape() const {
1488 343001 : return flags & Shape::ACCESSOR_SHAPE;
1489 : }
1490 :
1491 47465 : HashNumber hash() const {
1492 47465 : HashNumber hash = uintptr_t(base);
1493 :
1494 : /* Accumulate from least to most random so the low bits are most random. */
1495 47465 : hash = mozilla::RotateLeft(hash, 4) ^ attrs;
1496 47465 : hash = mozilla::RotateLeft(hash, 4) ^ slot_;
1497 47465 : hash = mozilla::RotateLeft(hash, 4) ^ HashId(propid);
1498 47466 : hash = mozilla::RotateLeft(hash, 4) ^ uintptr_t(rawGetter);
1499 47466 : hash = mozilla::RotateLeft(hash, 4) ^ uintptr_t(rawSetter);
1500 47467 : return hash;
1501 : }
1502 :
1503 : // Traceable implementation.
1504 : static void trace(StackShape* stackShape, JSTracer* trc) { stackShape->trace(trc); }
1505 : void trace(JSTracer* trc);
1506 : };
1507 :
1508 : template <typename Wrapper>
1509 416555 : class WrappedPtrOperations<StackShape, Wrapper>
1510 : {
1511 377091 : const StackShape& ss() const { return static_cast<const Wrapper*>(this)->get(); }
1512 :
1513 : public:
1514 120125 : bool hasSlot() const { return ss().hasSlot(); }
1515 102310 : bool hasMissingSlot() const { return ss().hasMissingSlot(); }
1516 1965 : uint32_t slot() const { return ss().slot(); }
1517 : uint32_t maybeSlot() const { return ss().maybeSlot(); }
1518 : uint32_t slotSpan() const { return ss().slotSpan(); }
1519 147860 : bool isAccessorShape() const { return ss().isAccessorShape(); }
1520 4830 : uint8_t attrs() const { return ss().attrs; }
1521 : };
1522 :
1523 : template <typename Wrapper>
1524 268339 : class MutableWrappedPtrOperations<StackShape, Wrapper>
1525 : : public WrappedPtrOperations<StackShape, Wrapper>
1526 : {
1527 239128 : StackShape& ss() { return static_cast<Wrapper*>(this)->get(); }
1528 :
1529 : public:
1530 117460 : void updateGetterSetter(GetterOp rawGetter, SetterOp rawSetter) {
1531 117460 : ss().updateGetterSetter(rawGetter, rawSetter);
1532 117460 : }
1533 117332 : void setSlot(uint32_t slot) { ss().setSlot(slot); }
1534 1895 : void setBase(UnownedBaseShape* base) { ss().base = base; }
1535 2415 : void setAttrs(uint8_t attrs) { ss().attrs = attrs; }
1536 : };
1537 :
1538 : inline
1539 95856 : Shape::Shape(const StackShape& other, uint32_t nfixed)
1540 191711 : : base_(other.base),
1541 : propid_(other.propid),
1542 95855 : slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)),
1543 95855 : attrs(other.attrs),
1544 95855 : flags(other.flags),
1545 575132 : parent(nullptr)
1546 : {
1547 : #ifdef DEBUG
1548 95855 : gc::AllocKind allocKind = getAllocKind();
1549 95856 : MOZ_ASSERT_IF(other.isAccessorShape(), allocKind == gc::AllocKind::ACCESSOR_SHAPE);
1550 95856 : MOZ_ASSERT_IF(allocKind == gc::AllocKind::SHAPE, !other.isAccessorShape());
1551 : #endif
1552 :
1553 95856 : MOZ_ASSERT_IF(!isEmptyShape(), AtomIsMarked(zone(), propid()));
1554 :
1555 95854 : MOZ_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
1556 95854 : kids.setNull();
1557 95854 : }
1558 :
1559 : // This class is used to add a post barrier on the AccessorShape's getter/setter
1560 : // objects. It updates the pointers and the shape's entry in the parent's
1561 : // KidsHash table.
1562 3350 : class ShapeGetterSetterRef : public gc::BufferableRef
1563 : {
1564 : AccessorShape* shape_;
1565 :
1566 : public:
1567 3350 : explicit ShapeGetterSetterRef(AccessorShape* shape) : shape_(shape) {}
1568 3129 : void trace(JSTracer* trc) override { shape_->fixupGetterSetterForBarrier(trc); }
1569 : };
1570 :
1571 : static inline void
1572 19563 : GetterSetterWriteBarrierPost(AccessorShape* shape)
1573 : {
1574 19563 : MOZ_ASSERT(shape);
1575 19563 : if (shape->hasGetterObject()) {
1576 18312 : gc::StoreBuffer* sb = reinterpret_cast<gc::Cell*>(shape->getterObject())->storeBuffer();
1577 18312 : if (sb) {
1578 3301 : sb->putGeneric(ShapeGetterSetterRef(shape));
1579 3301 : return;
1580 : }
1581 : }
1582 16262 : if (shape->hasSetterObject()) {
1583 7022 : gc::StoreBuffer* sb = reinterpret_cast<gc::Cell*>(shape->setterObject())->storeBuffer();
1584 7022 : if (sb) {
1585 49 : sb->putGeneric(ShapeGetterSetterRef(shape));
1586 49 : return;
1587 : }
1588 : }
1589 : }
1590 :
1591 : inline
1592 19446 : AccessorShape::AccessorShape(const StackShape& other, uint32_t nfixed)
1593 : : Shape(other, nfixed),
1594 19446 : rawGetter(other.rawGetter),
1595 38892 : rawSetter(other.rawSetter)
1596 : {
1597 19446 : MOZ_ASSERT(getAllocKind() == gc::AllocKind::ACCESSOR_SHAPE);
1598 19446 : GetterSetterWriteBarrierPost(this);
1599 19446 : }
1600 :
1601 : inline
1602 10182 : Shape::Shape(UnownedBaseShape* base, uint32_t nfixed)
1603 : : base_(base),
1604 : propid_(JSID_EMPTY),
1605 10182 : slotInfo(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)),
1606 : attrs(JSPROP_SHARED),
1607 : flags(0),
1608 20364 : parent(nullptr)
1609 : {
1610 10182 : MOZ_ASSERT(base);
1611 10182 : kids.setNull();
1612 10182 : }
1613 :
1614 : inline GetterOp
1615 379895 : Shape::getter() const
1616 : {
1617 379895 : return isAccessorShape() ? asAccessorShape().rawGetter : nullptr;
1618 : }
1619 :
1620 : inline SetterOp
1621 242332 : Shape::setter() const
1622 : {
1623 242332 : return isAccessorShape() ? asAccessorShape().rawSetter : nullptr;
1624 : }
1625 :
1626 : inline JSObject*
1627 46853 : Shape::getterObject() const
1628 : {
1629 46853 : MOZ_ASSERT(hasGetterValue());
1630 46853 : return asAccessorShape().getterObj;
1631 : }
1632 :
1633 : inline JSObject*
1634 24544 : Shape::setterObject() const
1635 : {
1636 24544 : MOZ_ASSERT(hasSetterValue());
1637 24544 : return asAccessorShape().setterObj;
1638 : }
1639 :
1640 : inline void
1641 23258 : Shape::initDictionaryShape(const StackShape& child, uint32_t nfixed, GCPtrShape* dictp)
1642 : {
1643 23258 : if (child.isAccessorShape())
1644 3511 : new (this) AccessorShape(child, nfixed);
1645 : else
1646 19747 : new (this) Shape(child, nfixed);
1647 23258 : this->flags |= IN_DICTIONARY;
1648 :
1649 23258 : this->listp = nullptr;
1650 23258 : if (dictp)
1651 22311 : insertIntoDictionary(dictp);
1652 23258 : }
1653 :
1654 : inline Shape*
1655 407249 : Shape::searchLinear(jsid id)
1656 : {
1657 6936573 : for (Shape* shape = this; shape; ) {
1658 6617547 : if (shape->propidRef() == id)
1659 88260 : return shape;
1660 6529358 : shape = shape->parent;
1661 : }
1662 :
1663 319026 : return nullptr;
1664 : }
1665 :
1666 : inline bool
1667 78626 : Shape::matches(const StackShape& other) const
1668 : {
1669 156212 : return propid_.get() == other.propid &&
1670 77586 : matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags,
1671 156212 : other.rawGetter, other.rawSetter);
1672 : }
1673 :
1674 : Shape*
1675 : ReshapeForAllocKind(JSContext* cx, Shape* shape, TaggedProto proto,
1676 : gc::AllocKind allocKind);
1677 :
1678 : } // namespace js
1679 :
1680 : #ifdef _MSC_VER
1681 : #pragma warning(pop)
1682 : #pragma warning(pop)
1683 : #endif
1684 :
1685 : // JS::ubi::Nodes can point to Shapes and BaseShapes; they're js::gc::Cell
1686 : // instances that occupy a compartment.
1687 : namespace JS {
1688 : namespace ubi {
1689 :
1690 : template<>
1691 : class Concrete<js::Shape> : TracerConcrete<js::Shape> {
1692 : protected:
1693 0 : explicit Concrete(js::Shape *ptr) : TracerConcrete<js::Shape>(ptr) { }
1694 :
1695 : public:
1696 0 : static void construct(void *storage, js::Shape *ptr) { new (storage) Concrete(ptr); }
1697 :
1698 : Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
1699 :
1700 0 : const char16_t* typeName() const override { return concreteTypeName; }
1701 : static const char16_t concreteTypeName[];
1702 : };
1703 :
1704 : template<>
1705 : class Concrete<js::BaseShape> : TracerConcrete<js::BaseShape> {
1706 : protected:
1707 0 : explicit Concrete(js::BaseShape *ptr) : TracerConcrete<js::BaseShape>(ptr) { }
1708 :
1709 : public:
1710 0 : static void construct(void *storage, js::BaseShape *ptr) { new (storage) Concrete(ptr); }
1711 :
1712 : Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
1713 :
1714 0 : const char16_t* typeName() const override { return concreteTypeName; }
1715 : static const char16_t concreteTypeName[];
1716 : };
1717 :
1718 : } // namespace ubi
1719 : } // namespace JS
1720 :
1721 : #endif /* vm_Shape_h */
|