1/*
2 * Copyright (C) 2008-2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "ClassInfo.h"
29#include "ConcurrentJSLock.h"
30#include "IndexingType.h"
31#include "JSCJSValue.h"
32#include "JSCast.h"
33#include "JSType.h"
34#include "PropertyName.h"
35#include "PropertyNameArray.h"
36#include "PropertyOffset.h"
37#include "PutPropertySlot.h"
38#include "StructureIDBlob.h"
39#include "StructureRareData.h"
40#include "StructureTransitionTable.h"
41#include "JSTypeInfo.h"
42#include "Watchpoint.h"
43#include "WriteBarrierInlines.h"
44#include <wtf/PrintStream.h>
45
46namespace WTF {
47
48class UniquedStringImpl;
49
50} // namespace WTF
51
52namespace JSC {
53
54class DeferGC;
55class LLIntOffsetsExtractor;
56class PropertyNameArray;
57class PropertyNameArrayData;
58class PropertyTable;
59class StructureChain;
60class StructureShape;
61class SlotVisitor;
62class JSString;
63struct DumpContext;
64
65// The out-of-line property storage capacity to use when first allocating out-of-line
66// storage. Note that all objects start out without having any out-of-line storage;
67// this comes into play only on the first property store that exhausts inline storage.
68static const unsigned initialOutOfLineCapacity = 4;
69
70// The factor by which to grow out-of-line storage when it is exhausted, after the
71// initial allocation.
72static const unsigned outOfLineGrowthFactor = 2;
73
74struct PropertyMapEntry {
75 UniquedStringImpl* key;
76 PropertyOffset offset;
77 uint8_t attributes;
78
79 PropertyMapEntry()
80 : key(nullptr)
81 , offset(invalidOffset)
82 , attributes(0)
83 {
84 }
85
86 PropertyMapEntry(UniquedStringImpl* key, PropertyOffset offset, unsigned attributes)
87 : key(key)
88 , offset(offset)
89 , attributes(attributes)
90 {
91 ASSERT(this->attributes == attributes);
92 }
93};
94
95class StructureFireDetail : public FireDetail {
96public:
97 StructureFireDetail(const Structure* structure)
98 : m_structure(structure)
99 {
100 }
101
102 void dump(PrintStream& out) const override;
103
104private:
105 const Structure* m_structure;
106};
107
108class DeferredStructureTransitionWatchpointFire : public DeferredWatchpointFire {
109 WTF_MAKE_NONCOPYABLE(DeferredStructureTransitionWatchpointFire);
110public:
111 JS_EXPORT_PRIVATE DeferredStructureTransitionWatchpointFire(VM&, Structure*);
112 JS_EXPORT_PRIVATE ~DeferredStructureTransitionWatchpointFire();
113
114 void dump(PrintStream& out) const override;
115
116 const Structure* structure() const { return m_structure; }
117
118private:
119 const Structure* m_structure;
120};
121
122class Structure final : public JSCell {
123public:
124 friend class StructureTransitionTable;
125
126 typedef JSCell Base;
127 static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
128
129 enum PolyProtoTag { PolyProto };
130 static Structure* create(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0);
131 static Structure* create(PolyProtoTag, VM&, JSGlobalObject*, JSObject* prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0);
132
133 ~Structure();
134
135 template<typename CellType, SubspaceAccess>
136 static IsoSubspace* subspaceFor(VM& vm)
137 {
138 return &vm.structureSpace;
139 }
140
141protected:
142 void finishCreation(VM& vm)
143 {
144 Base::finishCreation(vm);
145 ASSERT(m_prototype.get().isEmpty() || m_prototype.isObject() || m_prototype.isNull());
146 }
147
148 void finishCreation(VM& vm, const Structure* previous)
149 {
150 this->finishCreation(vm);
151 if (previous->hasRareData()) {
152 const StructureRareData* previousRareData = previous->rareData();
153 if (previousRareData->hasSharedPolyProtoWatchpoint()) {
154 ensureRareData(vm);
155 rareData()->setSharedPolyProtoWatchpoint(previousRareData->copySharedPolyProtoWatchpoint());
156 }
157 }
158 }
159
160 void finishCreation(VM& vm, CreatingEarlyCellTag)
161 {
162 Base::finishCreation(vm, this, CreatingEarlyCell);
163 ASSERT(m_prototype);
164 ASSERT(m_prototype.isNull());
165 ASSERT(!vm.structureStructure);
166 }
167
168public:
169 StructureID id() const { return m_blob.structureID(); }
170 int32_t objectInitializationBlob() const { return m_blob.blobExcludingStructureID(); }
171 int64_t idBlob() const { return m_blob.blob(); }
172
173 bool isProxy() const
174 {
175 JSType type = m_blob.type();
176 return type == ImpureProxyType || type == PureForwardingProxyType || type == ProxyObjectType;
177 }
178
179 static void dumpStatistics();
180
181 JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&);
182 JS_EXPORT_PRIVATE static Structure* addNewPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext, DeferredStructureTransitionWatchpointFire* = nullptr);
183 static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&);
184 JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, PropertyOffset&);
185 static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&);
186 static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype, DeferredStructureTransitionWatchpointFire&);
187 JS_EXPORT_PRIVATE static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes);
188 JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr);
189 static Structure* toUncacheableDictionaryTransition(VM&, Structure*);
190 JS_EXPORT_PRIVATE static Structure* sealTransition(VM&, Structure*);
191 JS_EXPORT_PRIVATE static Structure* freezeTransition(VM&, Structure*);
192 static Structure* preventExtensionsTransition(VM&, Structure*);
193 static Structure* nonPropertyTransition(VM&, Structure*, NonPropertyTransition);
194 JS_EXPORT_PRIVATE static Structure* nonPropertyTransitionSlow(VM&, Structure*, NonPropertyTransition);
195
196 JS_EXPORT_PRIVATE bool isSealed(VM&);
197 JS_EXPORT_PRIVATE bool isFrozen(VM&);
198 bool isStructureExtensible() const { return !didPreventExtensions(); }
199
200 JS_EXPORT_PRIVATE Structure* flattenDictionaryStructure(VM&, JSObject*);
201
202 static const bool needsDestruction = true;
203 static void destroy(JSCell*);
204
205 // Versions that take a func will call it after making the change but while still holding
206 // the lock. The callback is not called if there is no change being made, like if you call
207 // removePropertyWithoutTransition() and the property is not found.
208 template<typename Func>
209 PropertyOffset addPropertyWithoutTransition(VM&, PropertyName, unsigned attributes, const Func&);
210 template<typename Func>
211 PropertyOffset removePropertyWithoutTransition(VM&, PropertyName, const Func&);
212 void setPrototypeWithoutTransition(VM&, JSValue prototype);
213
214 bool isDictionary() const { return dictionaryKind() != NoneDictionaryKind; }
215 bool isUncacheableDictionary() const { return dictionaryKind() == UncachedDictionaryKind; }
216
217 bool prototypeQueriesAreCacheable()
218 {
219 return !typeInfo().prohibitsPropertyCaching();
220 }
221
222 bool propertyAccessesAreCacheable()
223 {
224 return dictionaryKind() != UncachedDictionaryKind
225 && prototypeQueriesAreCacheable()
226 && !(typeInfo().getOwnPropertySlotIsImpure() && !typeInfo().newImpurePropertyFiresWatchpoints());
227 }
228
229 bool propertyAccessesAreCacheableForAbsence()
230 {
231 return !typeInfo().getOwnPropertySlotIsImpureForPropertyAbsence();
232 }
233
234 bool needImpurePropertyWatchpoint()
235 {
236 return propertyAccessesAreCacheable()
237 && typeInfo().getOwnPropertySlotIsImpure()
238 && typeInfo().newImpurePropertyFiresWatchpoints();
239 }
240
241 bool isImmutablePrototypeExoticObject()
242 {
243 return typeInfo().isImmutablePrototypeExoticObject();
244 }
245
246 // We use SlowPath in GetByIdStatus for structures that may get new impure properties later to prevent
247 // DFG from inlining property accesses since structures don't transition when a new impure property appears.
248 bool takesSlowPathInDFGForImpureProperty()
249 {
250 return typeInfo().getOwnPropertySlotIsImpure();
251 }
252
253 // Type accessors.
254 TypeInfo typeInfo() const { return m_blob.typeInfo(m_outOfLineTypeFlags); }
255 bool isObject() const { return typeInfo().isObject(); }
256
257 IndexingType indexingType() const { return m_blob.indexingModeIncludingHistory() & AllWritableArrayTypes; }
258 IndexingType indexingMode() const { return m_blob.indexingModeIncludingHistory() & AllArrayTypes; }
259 IndexingType indexingModeIncludingHistory() const { return m_blob.indexingModeIncludingHistory(); }
260
261 inline bool mayInterceptIndexedAccesses() const;
262
263 bool holesMustForwardToPrototype(VM&, JSObject*) const;
264
265 JSGlobalObject* globalObject() const { return m_globalObject.get(); }
266
267 // NOTE: This method should only be called during the creation of structures, since the global
268 // object of a structure is presumed to be immutable in a bunch of places.
269 void setGlobalObject(VM&, JSGlobalObject*);
270
271 ALWAYS_INLINE bool hasMonoProto() const
272 {
273 return !m_prototype.get().isEmpty();
274 }
275 ALWAYS_INLINE bool hasPolyProto() const
276 {
277 return !hasMonoProto();
278 }
279 ALWAYS_INLINE JSValue storedPrototype() const
280 {
281 ASSERT(hasMonoProto());
282 return m_prototype.get();
283 }
284 JSValue storedPrototype(const JSObject*) const;
285 JSObject* storedPrototypeObject(const JSObject*) const;
286 Structure* storedPrototypeStructure(const JSObject*) const;
287
288 JSObject* storedPrototypeObject() const;
289 Structure* storedPrototypeStructure() const;
290 JSValue prototypeForLookup(JSGlobalObject*) const;
291 JSValue prototypeForLookup(JSGlobalObject*, JSCell* base) const;
292 StructureChain* prototypeChain(VM&, JSGlobalObject*, JSObject* base) const;
293 StructureChain* prototypeChain(ExecState*, JSObject* base) const;
294 static void visitChildren(JSCell*, SlotVisitor&);
295
296 // A Structure is cheap to mark during GC if doing so would only add a small and bounded amount
297 // to our heap footprint. For example, if the structure refers to a global object that is not
298 // yet marked, then as far as we know, the decision to mark this Structure would lead to a large
299 // increase in footprint because no other object refers to that global object. This method
300 // returns true if all user-controlled (and hence unbounded in size) objects referenced from the
301 // Structure are already marked.
302 bool isCheapDuringGC(VM&);
303
304 // Returns true if this structure is now marked.
305 bool markIfCheap(SlotVisitor&);
306
307 bool hasRareData() const
308 {
309 return isRareData(m_previousOrRareData.get());
310 }
311
312 StructureRareData* rareData()
313 {
314 ASSERT(hasRareData());
315 return static_cast<StructureRareData*>(m_previousOrRareData.get());
316 }
317
318 const StructureRareData* rareData() const
319 {
320 ASSERT(hasRareData());
321 return static_cast<const StructureRareData*>(m_previousOrRareData.get());
322 }
323
324 const StructureRareData* rareDataConcurrently() const
325 {
326 JSCell* cell = m_previousOrRareData.get();
327 if (isRareData(cell))
328 return static_cast<StructureRareData*>(cell);
329 return nullptr;
330 }
331
332 StructureRareData* ensureRareData(VM& vm)
333 {
334 if (!hasRareData())
335 allocateRareData(vm);
336 return rareData();
337 }
338
339 Structure* previousID() const
340 {
341 ASSERT(structure()->classInfo() == info());
342 // This is so written because it's used concurrently. We only load from m_previousOrRareData
343 // once, and this load is guaranteed atomic.
344 JSCell* cell = m_previousOrRareData.get();
345 if (isRareData(cell))
346 return static_cast<StructureRareData*>(cell)->previousID();
347 return static_cast<Structure*>(cell);
348 }
349 bool transitivelyTransitionedFrom(Structure* structureToFind);
350
351 PropertyOffset lastOffset() const { return m_offset; }
352
353 void setLastOffset(PropertyOffset offset) { m_offset = offset; }
354
355 static unsigned outOfLineCapacity(PropertyOffset lastOffset)
356 {
357 unsigned outOfLineSize = Structure::outOfLineSize(lastOffset);
358
359 // This algorithm completely determines the out-of-line property storage growth algorithm.
360 // The JSObject code will only trigger a resize if the value returned by this algorithm
361 // changed between the new and old structure. So, it's important to keep this simple because
362 // it's on a fast path.
363
364 if (!outOfLineSize)
365 return 0;
366
367 if (outOfLineSize <= initialOutOfLineCapacity)
368 return initialOutOfLineCapacity;
369
370 ASSERT(outOfLineSize > initialOutOfLineCapacity);
371 COMPILE_ASSERT(outOfLineGrowthFactor == 2, outOfLineGrowthFactor_is_two);
372 return WTF::roundUpToPowerOfTwo(outOfLineSize);
373 }
374
375 static unsigned outOfLineSize(PropertyOffset lastOffset)
376 {
377 return numberOfOutOfLineSlotsForLastOffset(lastOffset);
378 }
379
380 unsigned outOfLineCapacity() const
381 {
382 return outOfLineCapacity(m_offset);
383 }
384 unsigned outOfLineSize() const
385 {
386 return outOfLineSize(m_offset);
387 }
388 bool hasInlineStorage() const
389 {
390 return !!m_inlineCapacity;
391 }
392 unsigned inlineCapacity() const
393 {
394 return m_inlineCapacity;
395 }
396 unsigned inlineSize() const
397 {
398 return std::min<unsigned>(m_offset + 1, m_inlineCapacity);
399 }
400 unsigned totalStorageSize() const
401 {
402 return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
403 }
404 unsigned totalStorageCapacity() const
405 {
406 ASSERT(structure()->classInfo() == info());
407 return outOfLineCapacity() + inlineCapacity();
408 }
409
410 bool isValidOffset(PropertyOffset offset) const
411 {
412 return JSC::isValidOffset(offset)
413 && offset <= m_offset
414 && (offset < m_inlineCapacity || offset >= firstOutOfLineOffset);
415 }
416
417 bool hijacksIndexingHeader() const
418 {
419 return isTypedView(m_classInfo->typedArrayStorageType);
420 }
421
422 bool couldHaveIndexingHeader() const
423 {
424 return hasIndexedProperties(indexingType())
425 || hijacksIndexingHeader();
426 }
427
428 bool hasIndexingHeader(const JSCell*) const;
429
430 bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject);
431
432 PropertyOffset get(VM&, PropertyName);
433 PropertyOffset get(VM&, PropertyName, unsigned& attributes);
434
435 // This is a somewhat internalish method. It will call your functor while possibly holding the
436 // Structure's lock. There is no guarantee whether the lock is held or not in any particular
437 // call. So, you have to assume the worst. Also, the functor returns true if it wishes for you
438 // to continue or false if it's done.
439 template<typename Functor>
440 void forEachPropertyConcurrently(const Functor&);
441
442 template<typename Functor>
443 void forEachProperty(VM&, const Functor&);
444
445 PropertyOffset getConcurrently(UniquedStringImpl* uid);
446 PropertyOffset getConcurrently(UniquedStringImpl* uid, unsigned& attributes);
447
448 Vector<PropertyMapEntry> getPropertiesConcurrently();
449
450 void setHasGetterSetterPropertiesWithProtoCheck(bool is__proto__)
451 {
452 setHasGetterSetterProperties(true);
453 if (!is__proto__)
454 setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
455 }
456
457 void setContainsReadOnlyProperties() { setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); }
458
459 void setHasCustomGetterSetterPropertiesWithProtoCheck(bool is__proto__)
460 {
461 setHasCustomGetterSetterProperties(true);
462 if (!is__proto__)
463 setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
464 }
465
466 bool isEmpty() const
467 {
468 ASSERT(checkOffsetConsistency());
469 return !JSC::isValidOffset(m_offset);
470 }
471
472 void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*);
473 JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const;
474 bool canCachePropertyNameEnumerator() const;
475 bool canAccessPropertiesQuicklyForEnumeration() const;
476
477 void setCachedOwnKeys(VM&, JSImmutableButterfly*);
478 JSImmutableButterfly* cachedOwnKeys() const;
479 JSImmutableButterfly* cachedOwnKeysIgnoringSentinel() const;
480 bool canCacheOwnKeys() const;
481
482 void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
483
484 JSString* objectToStringValue()
485 {
486 if (!hasRareData())
487 return 0;
488 return rareData()->objectToStringValue();
489 }
490
491 void setObjectToStringValue(ExecState*, VM&, JSString* value, PropertySlot toStringTagSymbolSlot);
492
493 const ClassInfo* classInfo() const { return m_classInfo; }
494
495 static ptrdiff_t structureIDOffset()
496 {
497 return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset();
498 }
499
500 static ptrdiff_t prototypeOffset()
501 {
502 return OBJECT_OFFSETOF(Structure, m_prototype);
503 }
504
505 static ptrdiff_t globalObjectOffset()
506 {
507 return OBJECT_OFFSETOF(Structure, m_globalObject);
508 }
509
510 static ptrdiff_t classInfoOffset()
511 {
512 return OBJECT_OFFSETOF(Structure, m_classInfo);
513 }
514
515 static ptrdiff_t indexingModeIncludingHistoryOffset()
516 {
517 return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingModeIncludingHistoryOffset();
518 }
519
520 static ptrdiff_t propertyTableUnsafeOffset()
521 {
522 return OBJECT_OFFSETOF(Structure, m_propertyTableUnsafe);
523 }
524
525 static ptrdiff_t inlineCapacityOffset()
526 {
527 return OBJECT_OFFSETOF(Structure, m_inlineCapacity);
528 }
529
530 static ptrdiff_t previousOrRareDataOffset()
531 {
532 return OBJECT_OFFSETOF(Structure, m_previousOrRareData);
533 }
534
535 static Structure* createStructure(VM&);
536
537 bool transitionWatchpointSetHasBeenInvalidated() const
538 {
539 return m_transitionWatchpointSet.hasBeenInvalidated();
540 }
541
542 bool transitionWatchpointSetIsStillValid() const
543 {
544 return m_transitionWatchpointSet.isStillValid();
545 }
546
547 bool dfgShouldWatchIfPossible() const
548 {
549 // FIXME: We would like to not watch things that are unprofitable to watch, like
550 // dictionaries. Unfortunately, we can't do such things: a dictionary could get flattened,
551 // in which case it will start to appear watchable and so the DFG will think that it is
552 // watching it. We should come up with a comprehensive story for not watching things that
553 // aren't profitable to watch.
554 // https://bugs.webkit.org/show_bug.cgi?id=133625
555
556 // - We don't watch Structures that either decided not to be watched, or whose predecessors
557 // decided not to be watched. This happens when a transition is fired while being watched.
558 if (transitionWatchpointIsLikelyToBeFired())
559 return false;
560
561 // - Don't watch Structures that had been dictionaries.
562 if (hasBeenDictionary())
563 return false;
564
565 return true;
566 }
567
568 bool dfgShouldWatch() const
569 {
570 return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid();
571 }
572
573 void addTransitionWatchpoint(Watchpoint* watchpoint) const
574 {
575 ASSERT(transitionWatchpointSetIsStillValid());
576 m_transitionWatchpointSet.add(watchpoint);
577 }
578
579 void didTransitionFromThisStructure(DeferredStructureTransitionWatchpointFire* = nullptr) const;
580
581 InlineWatchpointSet& transitionWatchpointSet() const
582 {
583 return m_transitionWatchpointSet;
584 }
585
586 WatchpointSet* ensurePropertyReplacementWatchpointSet(VM&, PropertyOffset);
587 void startWatchingPropertyForReplacements(VM& vm, PropertyOffset offset)
588 {
589 ensurePropertyReplacementWatchpointSet(vm, offset);
590 }
591 void startWatchingPropertyForReplacements(VM&, PropertyName);
592 WatchpointSet* propertyReplacementWatchpointSet(PropertyOffset);
593 void didReplaceProperty(PropertyOffset);
594 void didCachePropertyReplacement(VM&, PropertyOffset);
595
596 void startWatchingInternalPropertiesIfNecessary(VM& vm)
597 {
598 if (LIKELY(didWatchInternalProperties()))
599 return;
600 startWatchingInternalProperties(vm);
601 }
602
603 Ref<StructureShape> toStructureShape(JSValue, bool& sawPolyProtoStructure);
604
605 void dump(PrintStream&) const;
606 void dumpInContext(PrintStream&, DumpContext*) const;
607 void dumpBrief(PrintStream&, const CString&) const;
608
609 static void dumpContextHeader(PrintStream&);
610
611 ConcurrentJSLock& lock() { return m_lock; }
612
613 unsigned propertyHash() const { return m_propertyHash; }
614
615 static bool shouldConvertToPolyProto(const Structure* a, const Structure* b);
616
617 DECLARE_EXPORT_INFO;
618
619private:
620 typedef enum {
621 NoneDictionaryKind = 0,
622 CachedDictionaryKind = 1,
623 UncachedDictionaryKind = 2
624 } DictionaryKind;
625
626public:
627#define DEFINE_BITFIELD(type, lowerName, upperName, width, offset) \
628 static const uint32_t s_##lowerName##Shift = offset;\
629 static const uint32_t s_##lowerName##Mask = ((1 << (width - 1)) | ((1 << (width - 1)) - 1));\
630 type lowerName() const { return static_cast<type>((m_bitField >> offset) & s_##lowerName##Mask); }\
631 void set##upperName(type newValue) \
632 {\
633 m_bitField &= ~(s_##lowerName##Mask << offset);\
634 m_bitField |= (newValue & s_##lowerName##Mask) << offset;\
635 }
636
637 DEFINE_BITFIELD(DictionaryKind, dictionaryKind, DictionaryKind, 2, 0);
638 DEFINE_BITFIELD(bool, isPinnedPropertyTable, IsPinnedPropertyTable, 1, 2);
639 DEFINE_BITFIELD(bool, hasGetterSetterProperties, HasGetterSetterProperties, 1, 3);
640 DEFINE_BITFIELD(bool, hasReadOnlyOrGetterSetterPropertiesExcludingProto, HasReadOnlyOrGetterSetterPropertiesExcludingProto, 1, 4);
641 DEFINE_BITFIELD(bool, isQuickPropertyAccessAllowedForEnumeration, IsQuickPropertyAccessAllowedForEnumeration, 1, 5);
642 DEFINE_BITFIELD(unsigned, attributesInPrevious, AttributesInPrevious, 14, 6);
643 DEFINE_BITFIELD(bool, didPreventExtensions, DidPreventExtensions, 1, 20);
644 DEFINE_BITFIELD(bool, didTransition, DidTransition, 1, 21);
645 DEFINE_BITFIELD(bool, staticPropertiesReified, StaticPropertiesReified, 1, 22);
646 DEFINE_BITFIELD(bool, hasBeenFlattenedBefore, HasBeenFlattenedBefore, 1, 23);
647 DEFINE_BITFIELD(bool, hasCustomGetterSetterProperties, HasCustomGetterSetterProperties, 1, 24);
648 DEFINE_BITFIELD(bool, didWatchInternalProperties, DidWatchInternalProperties, 1, 25);
649 DEFINE_BITFIELD(bool, transitionWatchpointIsLikelyToBeFired, TransitionWatchpointIsLikelyToBeFired, 1, 26);
650 DEFINE_BITFIELD(bool, hasBeenDictionary, HasBeenDictionary, 1, 27);
651 DEFINE_BITFIELD(bool, isAddingPropertyForTransition, IsAddingPropertyForTransition, 1, 28);
652 DEFINE_BITFIELD(bool, hasUnderscoreProtoPropertyExcludingOriginalProto, HasUnderscoreProtoPropertyExcludingOriginalProto, 1, 29);
653
654private:
655 friend class LLIntOffsetsExtractor;
656
657 JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity);
658 Structure(VM&);
659 Structure(VM&, Structure*, DeferredStructureTransitionWatchpointFire*);
660
661 static Structure* create(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr);
662
663 static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&);
664
665 // This will return the structure that has a usable property table, that property table,
666 // and the list of structures that we visited before we got to it. If it returns a
667 // non-null structure, it will also lock the structure that it returns; it is your job
668 // to unlock it.
669 void findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*&, PropertyTable*&);
670
671 static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind, DeferredStructureTransitionWatchpointFire* = nullptr);
672
673 enum class ShouldPin { No, Yes };
674 template<ShouldPin, typename Func>
675 PropertyOffset add(VM&, PropertyName, unsigned attributes, const Func&);
676 PropertyOffset add(VM&, PropertyName, unsigned attributes);
677 template<typename Func>
678 PropertyOffset remove(PropertyName, const Func&);
679 PropertyOffset remove(PropertyName);
680
681 void checkConsistency();
682
683 // This may grab the lock, or not. Do not call when holding the Structure's lock.
684 PropertyTable* ensurePropertyTableIfNotEmpty(VM& vm)
685 {
686 if (PropertyTable* result = m_propertyTableUnsafe.get())
687 return result;
688 if (!previousID())
689 return nullptr;
690 return materializePropertyTable(vm);
691 }
692
693 // This may grab the lock, or not. Do not call when holding the Structure's lock.
694 PropertyTable* ensurePropertyTable(VM& vm)
695 {
696 if (PropertyTable* result = m_propertyTableUnsafe.get())
697 return result;
698 return materializePropertyTable(vm);
699 }
700
701 PropertyTable* propertyTableOrNull() const
702 {
703 return m_propertyTableUnsafe.get();
704 }
705
706 // This will grab the lock. Do not call when holding the Structure's lock.
707 JS_EXPORT_PRIVATE PropertyTable* materializePropertyTable(VM&, bool setPropertyTable = true);
708
709 void setPropertyTable(VM& vm, PropertyTable* table);
710
711 PropertyTable* takePropertyTableOrCloneIfPinned(VM&);
712 PropertyTable* copyPropertyTableForPinning(VM&);
713
714 void setPreviousID(VM&, Structure*);
715
716 void clearPreviousID()
717 {
718 if (hasRareData())
719 rareData()->clearPreviousID();
720 else
721 m_previousOrRareData.clear();
722 }
723
724 int transitionCount() const
725 {
726 // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
727 return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
728 }
729
730 bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain, JSObject* base) const;
731
732 // You have to hold the structure lock to do these.
733 JS_EXPORT_PRIVATE void pin(const AbstractLocker&, VM&, PropertyTable*);
734 void pinForCaching(const AbstractLocker&, VM&, PropertyTable*);
735
736 bool isRareData(JSCell* cell) const
737 {
738 return cell && cell->structureID() != structureID();
739 }
740
741 template<typename DetailsFunc>
742 bool checkOffsetConsistency(PropertyTable*, const DetailsFunc&) const;
743 bool checkOffsetConsistency() const;
744
745 JS_EXPORT_PRIVATE void allocateRareData(VM&);
746
747 void startWatchingInternalProperties(VM&);
748
749 static const int s_maxTransitionLength = 64;
750 static const int s_maxTransitionLengthForNonEvalPutById = 512;
751
752 // These need to be properly aligned at the beginning of the 'Structure'
753 // part of the object.
754 StructureIDBlob m_blob;
755 TypeInfo::OutOfLineTypeFlags m_outOfLineTypeFlags;
756
757 uint8_t m_inlineCapacity;
758
759 ConcurrentJSLock m_lock;
760
761 uint32_t m_bitField;
762
763 WriteBarrier<JSGlobalObject> m_globalObject;
764 WriteBarrier<Unknown> m_prototype;
765 mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
766
767 WriteBarrier<JSCell> m_previousOrRareData;
768
769 RefPtr<UniquedStringImpl> m_nameInPrevious;
770
771 const ClassInfo* m_classInfo;
772
773 StructureTransitionTable m_transitionTable;
774
775 // Should be accessed through ensurePropertyTable(). During GC, it may be set to 0 by another thread.
776 // During a Heap Snapshot GC we avoid clearing the table so it is safe to use.
777 WriteBarrier<PropertyTable> m_propertyTableUnsafe;
778
779 mutable InlineWatchpointSet m_transitionWatchpointSet;
780
781 COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits);
782
783 // m_offset does not account for anonymous slots
784 PropertyOffset m_offset;
785
786 uint32_t m_propertyHash;
787};
788
789// We deliberately put Structure::create here in Structure.h instead of StructureInlines.h, because
790// it is used everywhere. This is so we don't have to hunt down all the places where we would need
791// to #include StructureInlines.h otherwise.
792inline Structure* Structure::create(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity)
793{
794 ASSERT(vm.structureStructure);
795 ASSERT(classInfo);
796 Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, globalObject, prototype, typeInfo, classInfo, indexingType, inlineCapacity);
797 structure->finishCreation(vm);
798 return structure;
799}
800
801} // namespace JSC
802