1/*
2 * Copyright (C) 2008, 2009, 2012-2016 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#ifndef Structure_h
27#define Structure_h
28
29#include "ClassInfo.h"
30#include "ConcurrentJITLock.h"
31#include "IndexingType.h"
32#include "InferredTypeTable.h"
33#include "JSCJSValue.h"
34#include "JSCell.h"
35#include "JSType.h"
36#include "PropertyName.h"
37#include "PropertyNameArray.h"
38#include "PropertyOffset.h"
39#include "Protect.h"
40#include "PutPropertySlot.h"
41#include "StructureIDBlob.h"
42#include "StructureRareData.h"
43#include "StructureRareDataInlines.h"
44#include "StructureTransitionTable.h"
45#include "JSTypeInfo.h"
46#include "Watchpoint.h"
47#include "Weak.h"
48#include "WriteBarrierInlines.h"
49#include <wtf/CompilationThread.h>
50#include <wtf/PassRefPtr.h>
51#include <wtf/PrintStream.h>
52#include <wtf/RefCounted.h>
53
54namespace WTF {
55
56class UniquedStringImpl;
57
58} // namespace WTF
59
60namespace JSC {
61
62class DeferGC;
63class LLIntOffsetsExtractor;
64class PropertyNameArray;
65class PropertyNameArrayData;
66class PropertyTable;
67class StructureChain;
68class StructureShape;
69class SlotVisitor;
70class JSString;
71struct DumpContext;
72
73// The out-of-line property storage capacity to use when first allocating out-of-line
74// storage. Note that all objects start out without having any out-of-line storage;
75// this comes into play only on the first property store that exhausts inline storage.
76static const unsigned initialOutOfLineCapacity = 4;
77
78// The factor by which to grow out-of-line storage when it is exhausted, after the
79// initial allocation.
80static const unsigned outOfLineGrowthFactor = 2;
81
82struct PropertyMapEntry {
83 UniquedStringImpl* key;
84 PropertyOffset offset;
85 uint8_t attributes;
86 bool hasInferredType; // This caches whether or not a property has an inferred type in the inferred type table, and is used for a fast check in JSObject::putDirectInternal().
87
88 PropertyMapEntry()
89 : key(nullptr)
90 , offset(invalidOffset)
91 , attributes(0)
92 , hasInferredType(false)
93 {
94 }
95
96 PropertyMapEntry(UniquedStringImpl* key, PropertyOffset offset, unsigned attributes)
97 : key(key)
98 , offset(offset)
99 , attributes(attributes)
100 , hasInferredType(false)
101 {
102 ASSERT(this->attributes == attributes);
103 }
104};
105
106class StructureFireDetail : public FireDetail {
107public:
108 StructureFireDetail(const Structure* structure)
109 : m_structure(structure)
110 {
111 }
112
113 virtual void dump(PrintStream& out) const override;
114
115private:
116 const Structure* m_structure;
117};
118
119class DeferredStructureTransitionWatchpointFire {
120 WTF_MAKE_NONCOPYABLE(DeferredStructureTransitionWatchpointFire);
121public:
122 JS_EXPORT_PRIVATE DeferredStructureTransitionWatchpointFire();
123 JS_EXPORT_PRIVATE ~DeferredStructureTransitionWatchpointFire();
124
125 void add(const Structure*);
126
127private:
128 const Structure* m_structure;
129};
130
131class Structure final : public JSCell {
132public:
133 friend class StructureTransitionTable;
134
135 typedef JSCell Base;
136 static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
137
138 static Structure* create(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0);
139
140 ~Structure();
141
142protected:
143 void finishCreation(VM& vm)
144 {
145 Base::finishCreation(vm);
146 ASSERT(m_prototype);
147 ASSERT(m_prototype.isObject() || m_prototype.isNull());
148 }
149
150 void finishCreation(VM& vm, CreatingEarlyCellTag)
151 {
152 Base::finishCreation(vm, this, CreatingEarlyCell);
153 ASSERT(m_prototype);
154 ASSERT(m_prototype.isNull());
155 ASSERT(!vm.structureStructure);
156 }
157
158public:
159 StructureID id() const { return m_blob.structureID(); }
160 int32_t objectInitializationBlob() const { return m_blob.blobExcludingStructureID(); }
161 int64_t idBlob() const { return m_blob.blob(); }
162
163 bool isProxy() const
164 {
165 JSType type = m_blob.type();
166 return type == ImpureProxyType || type == PureForwardingProxyType;
167 }
168
169 static void dumpStatistics();
170
171 JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext, DeferredStructureTransitionWatchpointFire* = nullptr);
172 static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&);
173 JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, PropertyOffset&);
174 static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&);
175 JS_EXPORT_PRIVATE static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype);
176 JS_EXPORT_PRIVATE static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes);
177 JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr);
178 static Structure* toUncacheableDictionaryTransition(VM&, Structure*);
179 JS_EXPORT_PRIVATE static Structure* sealTransition(VM&, Structure*);
180 JS_EXPORT_PRIVATE static Structure* freezeTransition(VM&, Structure*);
181 static Structure* preventExtensionsTransition(VM&, Structure*);
182 JS_EXPORT_PRIVATE static Structure* nonPropertyTransition(VM&, Structure*, NonPropertyTransition);
183
184 JS_EXPORT_PRIVATE bool isSealed(VM&);
185 JS_EXPORT_PRIVATE bool isFrozen(VM&);
186 bool isExtensible() const { return !preventExtensions(); }
187 bool putWillGrowOutOfLineStorage();
188 size_t suggestedNewOutOfLineStorageCapacity();
189
190 JS_EXPORT_PRIVATE Structure* flattenDictionaryStructure(VM&, JSObject*);
191
192 static const bool needsDestruction = true;
193 static void destroy(JSCell*);
194
195 // These should be used with caution.
196 JS_EXPORT_PRIVATE PropertyOffset addPropertyWithoutTransition(VM&, PropertyName, unsigned attributes);
197 PropertyOffset removePropertyWithoutTransition(VM&, PropertyName);
198 void setPrototypeWithoutTransition(VM& vm, JSValue prototype) { m_prototype.set(vm, this, prototype); }
199
200 bool isDictionary() const { return dictionaryKind() != NoneDictionaryKind; }
201 bool isUncacheableDictionary() const { return dictionaryKind() == UncachedDictionaryKind; }
202
203 bool propertyAccessesAreCacheable()
204 {
205 return dictionaryKind() != UncachedDictionaryKind
206 && !typeInfo().prohibitsPropertyCaching()
207 && !(typeInfo().getOwnPropertySlotIsImpure() && !typeInfo().newImpurePropertyFiresWatchpoints());
208 }
209
210 bool propertyAccessesAreCacheableForAbsence()
211 {
212 return !typeInfo().getOwnPropertySlotIsImpureForPropertyAbsence();
213 }
214
215 bool needImpurePropertyWatchpoint()
216 {
217 return propertyAccessesAreCacheable()
218 && typeInfo().getOwnPropertySlotIsImpure()
219 && typeInfo().newImpurePropertyFiresWatchpoints();
220 }
221
222 // We use SlowPath in GetByIdStatus for structures that may get new impure properties later to prevent
223 // DFG from inlining property accesses since structures don't transition when a new impure property appears.
224 bool takesSlowPathInDFGForImpureProperty()
225 {
226 return typeInfo().getOwnPropertySlotIsImpure();
227 }
228
229 // Type accessors.
230 TypeInfo typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_blob.typeInfo(m_outOfLineTypeFlags); }
231 bool isObject() const { return typeInfo().isObject(); }
232
233 IndexingType indexingType() const { return m_blob.indexingType() & AllArrayTypes; }
234 IndexingType indexingTypeIncludingHistory() const { return m_blob.indexingType(); }
235
236 bool mayInterceptIndexedAccesses() const
237 {
238 return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors);
239 }
240
241 JS_EXPORT_PRIVATE bool anyObjectInChainMayInterceptIndexedAccesses() const;
242 bool holesMustForwardToPrototype(VM&) const;
243
244 bool needsSlowPutIndexing() const;
245 NonPropertyTransition suggestedArrayStorageTransition() const;
246
247 JSGlobalObject* globalObject() const { return m_globalObject.get(); }
248 void setGlobalObject(VM& vm, JSGlobalObject* globalObject) { m_globalObject.set(vm, this, globalObject); }
249
250 JSValue storedPrototype() const { return m_prototype.get(); }
251 JSObject* storedPrototypeObject() const;
252 Structure* storedPrototypeStructure() const;
253 JSValue prototypeForLookup(ExecState*) const;
254 JSValue prototypeForLookup(JSGlobalObject*) const;
255 JSValue prototypeForLookup(CodeBlock*) const;
256 StructureChain* prototypeChain(VM&, JSGlobalObject*) const;
257 StructureChain* prototypeChain(ExecState*) const;
258 static void visitChildren(JSCell*, SlotVisitor&);
259
260 // Will just the prototype chain intercept this property access?
261 JS_EXPORT_PRIVATE bool prototypeChainMayInterceptStoreTo(VM&, PropertyName);
262
263 Structure* previousID() const
264 {
265 ASSERT(structure()->classInfo() == info());
266 if (hasRareData())
267 return rareData()->previousID();
268 return previous();
269 }
270 bool transitivelyTransitionedFrom(Structure* structureToFind);
271
272 unsigned outOfLineCapacity() const
273 {
274 ASSERT(checkOffsetConsistency());
275
276 unsigned outOfLineSize = this->outOfLineSize();
277
278 if (!outOfLineSize)
279 return 0;
280
281 if (outOfLineSize <= initialOutOfLineCapacity)
282 return initialOutOfLineCapacity;
283
284 ASSERT(outOfLineSize > initialOutOfLineCapacity);
285 COMPILE_ASSERT(outOfLineGrowthFactor == 2, outOfLineGrowthFactor_is_two);
286 return WTF::roundUpToPowerOfTwo(outOfLineSize);
287 }
288 unsigned outOfLineSize() const
289 {
290 ASSERT(checkOffsetConsistency());
291 ASSERT(structure()->classInfo() == info());
292
293 return numberOfOutOfLineSlotsForLastOffset(m_offset);
294 }
295 bool hasInlineStorage() const
296 {
297 return !!m_inlineCapacity;
298 }
299 unsigned inlineCapacity() const
300 {
301 return m_inlineCapacity;
302 }
303 unsigned inlineSize() const
304 {
305 return std::min<unsigned>(m_offset + 1, m_inlineCapacity);
306 }
307 unsigned totalStorageSize() const
308 {
309 return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
310 }
311 unsigned totalStorageCapacity() const
312 {
313 ASSERT(structure()->classInfo() == info());
314 return outOfLineCapacity() + inlineCapacity();
315 }
316
317 bool isValidOffset(PropertyOffset offset) const
318 {
319 return JSC::isValidOffset(offset)
320 && offset <= m_offset
321 && (offset < m_inlineCapacity || offset >= firstOutOfLineOffset);
322 }
323
324 bool hijacksIndexingHeader() const
325 {
326 return isTypedView(m_classInfo->typedArrayStorageType);
327 }
328
329 bool couldHaveIndexingHeader() const
330 {
331 return hasIndexedProperties(indexingType())
332 || hijacksIndexingHeader();
333 }
334
335 bool hasIndexingHeader(const JSCell*) const;
336
337 bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject);
338
339 PropertyOffset get(VM&, PropertyName);
340 PropertyOffset get(VM&, PropertyName, unsigned& attributes);
341 PropertyOffset get(VM&, PropertyName, unsigned& attributes, bool& hasInferredType);
342
343 // This is a somewhat internalish method. It will call your functor while possibly holding the
344 // Structure's lock. There is no guarantee whether the lock is held or not in any particular
345 // call. So, you have to assume the worst. Also, the functor returns true if it wishes for you
346 // to continue or false if it's done.
347 template<typename Functor>
348 void forEachPropertyConcurrently(const Functor&);
349
350 PropertyOffset getConcurrently(UniquedStringImpl* uid);
351 PropertyOffset getConcurrently(UniquedStringImpl* uid, unsigned& attributes);
352
353 Vector<PropertyMapEntry> getPropertiesConcurrently();
354
355 void setHasGetterSetterPropertiesWithProtoCheck(bool is__proto__)
356 {
357 setHasGetterSetterProperties(true);
358 if (!is__proto__)
359 setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
360 }
361
362 void setContainsReadOnlyProperties() { setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); }
363
364 void setHasCustomGetterSetterPropertiesWithProtoCheck(bool is__proto__)
365 {
366 setHasCustomGetterSetterProperties(true);
367 if (!is__proto__)
368 setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
369 }
370
371 bool isEmpty() const
372 {
373 ASSERT(checkOffsetConsistency());
374 return !JSC::isValidOffset(m_offset);
375 }
376
377 void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*);
378 JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const;
379 bool canCachePropertyNameEnumerator() const;
380 bool canAccessPropertiesQuickly() const;
381
382 void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
383
384 JSString* objectToStringValue()
385 {
386 if (!hasRareData())
387 return 0;
388 return rareData()->objectToStringValue();
389 }
390
391 void setObjectToStringValue(ExecState*, VM&, JSString* value, PropertySlot toStringTagSymbolSlot);
392
393 const ClassInfo* classInfo() const { return m_classInfo; }
394
395 static ptrdiff_t structureIDOffset()
396 {
397 return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset();
398 }
399
400 static ptrdiff_t prototypeOffset()
401 {
402 return OBJECT_OFFSETOF(Structure, m_prototype);
403 }
404
405 static ptrdiff_t globalObjectOffset()
406 {
407 return OBJECT_OFFSETOF(Structure, m_globalObject);
408 }
409
410 static ptrdiff_t classInfoOffset()
411 {
412 return OBJECT_OFFSETOF(Structure, m_classInfo);
413 }
414
415 static ptrdiff_t indexingTypeOffset()
416 {
417 return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingTypeOffset();
418 }
419
420 static Structure* createStructure(VM&);
421
422 bool transitionWatchpointSetHasBeenInvalidated() const
423 {
424 return m_transitionWatchpointSet.hasBeenInvalidated();
425 }
426
427 bool transitionWatchpointSetIsStillValid() const
428 {
429 return m_transitionWatchpointSet.isStillValid();
430 }
431
432 bool dfgShouldWatchIfPossible() const
433 {
434 // FIXME: We would like to not watch things that are unprofitable to watch, like
435 // dictionaries. Unfortunately, we can't do such things: a dictionary could get flattened,
436 // in which case it will start to appear watchable and so the DFG will think that it is
437 // watching it. We should come up with a comprehensive story for not watching things that
438 // aren't profitable to watch.
439 // https://bugs.webkit.org/show_bug.cgi?id=133625
440
441 // - We don't watch Structures that either decided not to be watched, or whose predecessors
442 // decided not to be watched. This happens either when a transition is fired while being
443 // watched.
444 if (transitionWatchpointIsLikelyToBeFired())
445 return false;
446
447 // - Don't watch Structures that had been dictionaries.
448 if (hasBeenDictionary())
449 return false;
450
451 return true;
452 }
453
454 bool dfgShouldWatch() const
455 {
456 return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid();
457 }
458
459 void addTransitionWatchpoint(Watchpoint* watchpoint) const
460 {
461 ASSERT(transitionWatchpointSetIsStillValid());
462 m_transitionWatchpointSet.add(watchpoint);
463 }
464
465 void didTransitionFromThisStructure(DeferredStructureTransitionWatchpointFire* = nullptr) const;
466
467 InlineWatchpointSet& transitionWatchpointSet() const
468 {
469 return m_transitionWatchpointSet;
470 }
471
472 WatchpointSet* ensurePropertyReplacementWatchpointSet(VM&, PropertyOffset);
473 void startWatchingPropertyForReplacements(VM& vm, PropertyOffset offset)
474 {
475 ensurePropertyReplacementWatchpointSet(vm, offset);
476 }
477 void startWatchingPropertyForReplacements(VM&, PropertyName);
478 WatchpointSet* propertyReplacementWatchpointSet(PropertyOffset);
479 void didReplaceProperty(PropertyOffset);
480 void didCachePropertyReplacement(VM&, PropertyOffset);
481
482 void startWatchingInternalPropertiesIfNecessary(VM& vm)
483 {
484 if (LIKELY(didWatchInternalProperties()))
485 return;
486 startWatchingInternalProperties(vm);
487 }
488
489 void startWatchingInternalPropertiesIfNecessaryForEntireChain(VM& vm)
490 {
491 for (Structure* structure = this; structure; structure = structure->storedPrototypeStructure())
492 structure->startWatchingInternalPropertiesIfNecessary(vm);
493 }
494
495 bool hasInferredTypes() const
496 {
497 return !!m_inferredTypeTable;
498 }
499
500 InferredType* inferredTypeFor(UniquedStringImpl* uid)
501 {
502 if (InferredTypeTable* table = m_inferredTypeTable.get())
503 return table->get(uid);
504 return nullptr;
505 }
506
507 InferredType::Descriptor inferredTypeDescriptorFor(UniquedStringImpl* uid)
508 {
509 if (InferredType* result = inferredTypeFor(uid))
510 return result->descriptor();
511 return InferredType::Top;
512 }
513
514 // Call this when we know that this is a brand new property. Note that it's not enough for the
515 // property to be brand new to some object. It has to be brand new to the Structure.
516 ALWAYS_INLINE void willStoreValueForNewTransition(
517 VM& vm, PropertyName propertyName, JSValue value, bool shouldOptimize)
518 {
519 if (hasBeenDictionary() || (!shouldOptimize && !m_inferredTypeTable))
520 return;
521 willStoreValueSlow(vm, propertyName, value, shouldOptimize, InferredTypeTable::NewProperty);
522 }
523
524 // Call this when we know that this is a new property for the object, but not new for the
525 // structure. Therefore, under the InferredTypeTable's rules, absence of the property from the
526 // table means Top rather than Bottom.
527 ALWAYS_INLINE void willStoreValueForExistingTransition(
528 VM& vm, PropertyName propertyName, JSValue value, bool shouldOptimize)
529 {
530 if (hasBeenDictionary() || !m_inferredTypeTable)
531 return;
532 willStoreValueSlow(vm, propertyName, value, shouldOptimize, InferredTypeTable::NewProperty);
533 }
534
535 // Call this when we know that the inferred type table exists and has an entry for this property.
536 ALWAYS_INLINE void willStoreValueForReplace(
537 VM& vm, PropertyName propertyName, JSValue value, bool shouldOptimize)
538 {
539 if (hasBeenDictionary())
540 return;
541 willStoreValueSlow(vm, propertyName, value, shouldOptimize, InferredTypeTable::OldProperty);
542 }
543
544 PassRefPtr<StructureShape> toStructureShape(JSValue);
545
546 // Determines if the two structures match enough that this one could be used for allocations
547 // of the other one.
548 bool canUseForAllocationsOf(Structure*);
549
550 void dump(PrintStream&) const;
551 void dumpInContext(PrintStream&, DumpContext*) const;
552 void dumpBrief(PrintStream&, const CString&) const;
553
554 static void dumpContextHeader(PrintStream&);
555
556 DECLARE_EXPORT_INFO;
557
558private:
559 typedef enum {
560 NoneDictionaryKind = 0,
561 CachedDictionaryKind = 1,
562 UncachedDictionaryKind = 2
563 } DictionaryKind;
564
565public:
566#define DEFINE_BITFIELD(type, lowerName, upperName, width, offset) \
567 static const uint32_t s_##lowerName##Shift = offset;\
568 static const uint32_t s_##lowerName##Mask = ((1 << (width - 1)) | ((1 << (width - 1)) - 1));\
569 type lowerName() const { return static_cast<type>((m_bitField >> offset) & s_##lowerName##Mask); }\
570 void set##upperName(type newValue) \
571 {\
572 m_bitField &= ~(s_##lowerName##Mask << offset);\
573 m_bitField |= (newValue & s_##lowerName##Mask) << offset;\
574 }
575
576 DEFINE_BITFIELD(DictionaryKind, dictionaryKind, DictionaryKind, 2, 0);
577 DEFINE_BITFIELD(bool, isPinnedPropertyTable, IsPinnedPropertyTable, 1, 2);
578 DEFINE_BITFIELD(bool, hasGetterSetterProperties, HasGetterSetterProperties, 1, 3);
579 DEFINE_BITFIELD(bool, hasReadOnlyOrGetterSetterPropertiesExcludingProto, HasReadOnlyOrGetterSetterPropertiesExcludingProto, 1, 4);
580 DEFINE_BITFIELD(bool, hasNonEnumerableProperties, HasNonEnumerableProperties, 1, 5);
581 DEFINE_BITFIELD(unsigned, attributesInPrevious, AttributesInPrevious, 14, 6);
582 DEFINE_BITFIELD(bool, preventExtensions, PreventExtensions, 1, 20);
583 DEFINE_BITFIELD(bool, didTransition, DidTransition, 1, 21);
584 DEFINE_BITFIELD(bool, staticFunctionsReified, StaticFunctionsReified, 1, 22);
585 DEFINE_BITFIELD(bool, hasRareData, HasRareData, 1, 23);
586 DEFINE_BITFIELD(bool, hasBeenFlattenedBefore, HasBeenFlattenedBefore, 1, 24);
587 DEFINE_BITFIELD(bool, hasCustomGetterSetterProperties, HasCustomGetterSetterProperties, 1, 25);
588 DEFINE_BITFIELD(bool, didWatchInternalProperties, DidWatchInternalProperties, 1, 26);
589 DEFINE_BITFIELD(bool, transitionWatchpointIsLikelyToBeFired, TransitionWatchpointIsLikelyToBeFired, 1, 27);
590 DEFINE_BITFIELD(bool, hasBeenDictionary, HasBeenDictionary, 1, 28);
591
592private:
593 friend class LLIntOffsetsExtractor;
594
595 JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity);
596 Structure(VM&);
597 Structure(VM&, Structure*, DeferredStructureTransitionWatchpointFire*);
598
599 static Structure* create(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr);
600
601 static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&);
602
603 // This will return the structure that has a usable property table, that property table,
604 // and the list of structures that we visited before we got to it. If it returns a
605 // non-null structure, it will also lock the structure that it returns; it is your job
606 // to unlock it.
607 void findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*&, PropertyTable*&);
608
609 static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind, DeferredStructureTransitionWatchpointFire* = nullptr);
610
611 PropertyOffset add(VM&, PropertyName, unsigned attributes);
612 PropertyOffset remove(PropertyName);
613
614 void createPropertyMap(const GCSafeConcurrentJITLocker&, VM&, unsigned keyCount = 0);
615 void checkConsistency();
616
617 WriteBarrier<PropertyTable>& propertyTable();
618 PropertyTable* takePropertyTableOrCloneIfPinned(VM&);
619 PropertyTable* copyPropertyTable(VM&);
620 PropertyTable* copyPropertyTableForPinning(VM&);
621 JS_EXPORT_PRIVATE void materializePropertyMap(VM&);
622 ALWAYS_INLINE void materializePropertyMapIfNecessary(VM& vm, DeferGC&)
623 {
624 ASSERT(!isCompilationThread());
625 ASSERT(structure()->classInfo() == info());
626 ASSERT(checkOffsetConsistency());
627 if (!propertyTable() && previousID())
628 materializePropertyMap(vm);
629 }
630 ALWAYS_INLINE void materializePropertyMapIfNecessary(VM& vm, PropertyTable*& table)
631 {
632 ASSERT(!isCompilationThread());
633 ASSERT(structure()->classInfo() == info());
634 ASSERT(checkOffsetConsistency());
635 table = propertyTable().get();
636 if (!table && previousID()) {
637 DeferGC deferGC(vm.heap);
638 materializePropertyMap(vm);
639 table = propertyTable().get();
640 }
641 }
642 void materializePropertyMapIfNecessaryForPinning(VM& vm, DeferGC&)
643 {
644 ASSERT(structure()->classInfo() == info());
645 checkOffsetConsistency();
646 if (!propertyTable())
647 materializePropertyMap(vm);
648 }
649
650 void setPreviousID(VM& vm, Structure* structure)
651 {
652 if (hasRareData())
653 rareData()->setPreviousID(vm, structure);
654 else
655 m_previousOrRareData.set(vm, this, structure);
656 }
657
658 void clearPreviousID()
659 {
660 if (hasRareData())
661 rareData()->clearPreviousID();
662 else
663 m_previousOrRareData.clear();
664 }
665
666 int transitionCount() const
667 {
668 // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
669 return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
670 }
671
672 bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const;
673 bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
674
675 void pin();
676
677 Structure* previous() const
678 {
679 ASSERT(!hasRareData());
680 return static_cast<Structure*>(m_previousOrRareData.get());
681 }
682
683 StructureRareData* rareData() const
684 {
685 ASSERT(hasRareData());
686 return static_cast<StructureRareData*>(m_previousOrRareData.get());
687 }
688
689 bool checkOffsetConsistency() const;
690
691 JS_EXPORT_PRIVATE void allocateRareData(VM&);
692
693 void startWatchingInternalProperties(VM&);
694
695 JS_EXPORT_PRIVATE void willStoreValueSlow(
696 VM&, PropertyName, JSValue, bool, InferredTypeTable::StoredPropertyAge);
697
698 static const int s_maxTransitionLength = 64;
699 static const int s_maxTransitionLengthForNonEvalPutById = 512;
700
701 // These need to be properly aligned at the beginning of the 'Structure'
702 // part of the object.
703 StructureIDBlob m_blob;
704 TypeInfo::OutOfLineTypeFlags m_outOfLineTypeFlags;
705
706 WriteBarrier<JSGlobalObject> m_globalObject;
707 WriteBarrier<Unknown> m_prototype;
708 mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
709
710 WriteBarrier<JSCell> m_previousOrRareData;
711
712 RefPtr<UniquedStringImpl> m_nameInPrevious;
713
714 const ClassInfo* m_classInfo;
715
716 StructureTransitionTable m_transitionTable;
717
718 // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread.
719 WriteBarrier<PropertyTable> m_propertyTableUnsafe;
720
721 WriteBarrier<InferredTypeTable> m_inferredTypeTable;
722
723 mutable InlineWatchpointSet m_transitionWatchpointSet;
724
725 COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits);
726
727 // m_offset does not account for anonymous slots
728 PropertyOffset m_offset;
729
730 uint8_t m_inlineCapacity;
731
732 ConcurrentJITLock m_lock;
733
734 uint32_t m_bitField;
735};
736
737} // namespace JSC
738
739#endif // Structure_h
740