3dc17b7335994e83e11102d0e9ecab0d6e498057
[WebKit-https.git] / Source / JavaScriptCore / runtime / StructureInlines.h
1 /*
2  * Copyright (C) 2013-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 "JSArrayBufferView.h"
29 #include "JSCJSValueInlines.h"
30 #include "JSGlobalObject.h"
31 #include "PropertyMapHashTable.h"
32 #include "Structure.h"
33 #include "StructureChain.h"
34 #include "StructureRareDataInlines.h"
35 #include <wtf/Threading.h>
36
37 namespace JSC {
38
39 inline Structure* Structure::create(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity)
40 {
41     ASSERT(vm.structureStructure);
42     ASSERT(classInfo);
43     if (auto* object = prototype.getObject()) {
44         ASSERT(!object->anyObjectInChainMayInterceptIndexedAccesses(vm) || hasSlowPutArrayStorage(indexingType) || !hasIndexedProperties(indexingType));
45         object->didBecomePrototype();
46     }
47
48     Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, globalObject, prototype, typeInfo, classInfo, indexingType, inlineCapacity);
49     structure->finishCreation(vm);
50     return structure;
51 }
52
53 inline Structure* Structure::createStructure(VM& vm)
54 {
55     ASSERT(!vm.structureStructure);
56     Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm);
57     structure->finishCreation(vm, CreatingEarlyCell);
58     return structure;
59 }
60
61 inline Structure* Structure::create(VM& vm, Structure* previous, DeferredStructureTransitionWatchpointFire* deferred)
62 {
63     ASSERT(vm.structureStructure);
64     Structure* newStructure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, previous, deferred);
65     newStructure->finishCreation(vm, previous);
66     return newStructure;
67 }
68
69 inline bool Structure::mayInterceptIndexedAccesses() const
70 {
71     if (indexingModeIncludingHistory() & MayHaveIndexedAccessors)
72         return true;
73
74     // Consider a scenario where object O (of global G1)'s prototype is set to A
75     // (of global G2), and G2 is already having a bad time. If an object B with
76     // indexed accessors is then set as the prototype of A:
77     //      O -> A -> B
78     // Then, O should be converted to SlowPutArrayStorage (because it now has an
79     // object with indexed accessors in its prototype chain). But it won't be
80     // converted because this conversion is done by JSGlobalObject::haveAbadTime(),
81     // but G2 is already having a bad time. We solve this by conservatively
82     // treating A as potentially having indexed accessors if its global is already
83     // having a bad time. Hence, when A is set as O's prototype, O will be
84     // converted to SlowPutArrayStorage.
85
86     JSGlobalObject* globalObject = this->globalObject();
87     if (!globalObject)
88         return false;
89     return globalObject->isHavingABadTime();
90 }
91
92 inline JSObject* Structure::storedPrototypeObject() const
93 {
94     ASSERT(hasMonoProto());
95     JSValue value = m_prototype.get();
96     if (value.isNull())
97         return nullptr;
98     return asObject(value);
99 }
100
101 inline Structure* Structure::storedPrototypeStructure() const
102 {
103     ASSERT(hasMonoProto());
104     JSObject* object = storedPrototypeObject();
105     if (!object)
106         return nullptr;
107     return object->structure();
108 }
109
110 ALWAYS_INLINE JSValue Structure::storedPrototype(const JSObject* object) const
111 {
112     ASSERT(isCompilationThread() || Thread::mayBeGCThread() || object->structure() == this);
113     if (hasMonoProto())
114         return storedPrototype();
115     return object->getDirect(knownPolyProtoOffset);
116 }
117
118 ALWAYS_INLINE JSObject* Structure::storedPrototypeObject(const JSObject* object) const
119 {
120     ASSERT(isCompilationThread() || Thread::mayBeGCThread() || object->structure() == this);
121     if (hasMonoProto())
122         return storedPrototypeObject();
123     JSValue proto = object->getDirect(knownPolyProtoOffset);
124     if (proto.isNull())
125         return nullptr;
126     return asObject(proto);
127 }
128
129 ALWAYS_INLINE Structure* Structure::storedPrototypeStructure(const JSObject* object) const
130 {
131     if (JSObject* proto = storedPrototypeObject(object))
132         return proto->structure();
133     return nullptr;
134 }
135
136 ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName)
137 {
138     unsigned attributes;
139     return get(vm, propertyName, attributes);
140 }
141     
142 ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes)
143 {
144     ASSERT(!isCompilationThread());
145     ASSERT(structure(vm)->classInfo() == info());
146
147     if (ruleOutUnseenProperty(propertyName.uid()))
148         return invalidOffset;
149
150     PropertyTable* propertyTable = ensurePropertyTableIfNotEmpty(vm);
151     if (!propertyTable)
152         return invalidOffset;
153
154     PropertyMapEntry* entry = propertyTable->get(propertyName.uid());
155     if (!entry)
156         return invalidOffset;
157
158     attributes = entry->attributes;
159     return entry->offset;
160 }
161
162 inline bool Structure::ruleOutUnseenProperty(UniquedStringImpl* uid) const
163 {
164     ASSERT(uid);
165     return seenProperties().ruleOut(bitwise_cast<uintptr_t>(uid));
166 }
167
168 inline TinyBloomFilter Structure::seenProperties() const
169 {
170 #if CPU(ADDRESS64)
171     return TinyBloomFilter(bitwise_cast<uintptr_t>(m_propertyHashAndSeenProperties.pointer()));
172 #else
173     return TinyBloomFilter(m_seenProperties);
174 #endif
175 }
176
177 inline void Structure::addPropertyHashAndSeenProperty(unsigned hash, UniquedStringImpl* pointer)
178 {
179 #if CPU(ADDRESS64)
180     m_propertyHashAndSeenProperties.setType(m_propertyHashAndSeenProperties.type() ^ hash);
181     m_propertyHashAndSeenProperties.setPointer(bitwise_cast<UniquedStringImpl*>(bitwise_cast<uintptr_t>(m_propertyHashAndSeenProperties.pointer()) | bitwise_cast<uintptr_t>(pointer)));
182 #else
183     m_propertyHash = m_propertyHash ^ hash;
184     m_seenProperties = bitwise_cast<uintptr_t>(pointer) | m_seenProperties;
185 #endif
186 }
187
188 template<typename Functor>
189 void Structure::forEachPropertyConcurrently(const Functor& functor)
190 {
191     Vector<Structure*, 8> structures;
192     Structure* tableStructure;
193     PropertyTable* table;
194     VM& vm = this->vm();
195     
196     findStructuresAndMapForMaterialization(vm, structures, tableStructure, table);
197
198     HashSet<UniquedStringImpl*> seenProperties;
199
200     for (Structure* structure : structures) {
201         UniquedStringImpl* transitionPropertyName = structure->transitionPropertyName();
202         if (!transitionPropertyName || seenProperties.contains(transitionPropertyName))
203             continue;
204
205         seenProperties.add(transitionPropertyName);
206
207         if (structure->isPropertyDeletionTransition())
208             continue;
209
210         if (!functor(PropertyMapEntry(transitionPropertyName, structure->transitionOffset(), structure->transitionPropertyAttributes()))) {
211             if (table)
212                 tableStructure->cellLock().unlock();
213             return;
214         }
215     }
216     
217     if (table) {
218         for (auto& entry : *table) {
219             if (seenProperties.contains(entry.key))
220                 continue;
221
222             if (!functor(entry)) {
223                 tableStructure->cellLock().unlock();
224                 return;
225             }
226         }
227         tableStructure->cellLock().unlock();
228     }
229 }
230
231 template<typename Functor>
232 void Structure::forEachProperty(VM& vm, const Functor& functor)
233 {
234     if (PropertyTable* table = ensurePropertyTableIfNotEmpty(vm)) {
235         for (auto& entry : *table) {
236             if (!functor(entry))
237                 return;
238         }
239     }
240 }
241
242 inline PropertyOffset Structure::getConcurrently(UniquedStringImpl* uid)
243 {
244     unsigned attributesIgnored;
245     return getConcurrently(uid, attributesIgnored);
246 }
247
248 inline bool Structure::hasIndexingHeader(const JSCell* cell) const
249 {
250     if (hasIndexedProperties(indexingType()))
251         return true;
252     
253     if (!isTypedView(typedArrayTypeForType(m_blob.type())))
254         return false;
255
256     return jsCast<const JSArrayBufferView*>(cell)->mode() == WastefulTypedArray;
257 }
258
259 inline bool Structure::mayHaveIndexingHeader(bool& mustCheckCell) const
260 {
261     mustCheckCell = false;
262     if (hasIndexedProperties(indexingType()))
263         return true;
264
265     if (!isTypedView(typedArrayTypeForType(m_blob.type())))
266         return false;
267
268     mustCheckCell = true;
269     return true;
270 }
271
272 inline bool Structure::masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject)
273 {
274     return typeInfo().masqueradesAsUndefined() && globalObject() == lexicalGlobalObject;
275 }
276
277 inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind)
278 {
279     VM& vm = this->vm();
280     for (Structure* current = this; current; current = current->previousID(vm)) {
281         if (current == structureToFind)
282             return true;
283     }
284     return false;
285 }
286
287 inline void Structure::setCachedOwnKeys(VM& vm, JSImmutableButterfly* ownKeys)
288 {
289     ensureRareData(vm)->setCachedOwnKeys(vm, ownKeys);
290 }
291
292 inline JSImmutableButterfly* Structure::cachedOwnKeys() const
293 {
294     if (!hasRareData())
295         return nullptr;
296     return rareData()->cachedOwnKeys();
297 }
298
299 inline JSImmutableButterfly* Structure::cachedOwnKeysIgnoringSentinel() const
300 {
301     if (!hasRareData())
302         return nullptr;
303     return rareData()->cachedOwnKeysIgnoringSentinel();
304 }
305
306 inline bool Structure::canCacheOwnKeys() const
307 {
308     if (isDictionary())
309         return false;
310     if (hasIndexedProperties(indexingType()))
311         return false;
312     if (typeInfo().overridesGetPropertyNames())
313         return false;
314     return true;
315 }
316
317 ALWAYS_INLINE JSValue prototypeForLookupPrimitiveImpl(JSGlobalObject* globalObject, const Structure* structure)
318 {
319     ASSERT(!structure->isObject());
320
321     if (structure->typeInfo().type() == StringType)
322         return globalObject->stringPrototype();
323     
324     if (structure->typeInfo().type() == BigIntType)
325         return globalObject->bigIntPrototype();
326
327     ASSERT(structure->typeInfo().type() == SymbolType);
328     return globalObject->symbolPrototype();
329 }
330
331 inline JSValue Structure::prototypeForLookup(JSGlobalObject* globalObject) const
332 {
333     ASSERT(hasMonoProto());
334     if (isObject())
335         return storedPrototype();
336     return prototypeForLookupPrimitiveImpl(globalObject, this);
337 }
338
339 inline JSValue Structure::prototypeForLookup(JSGlobalObject* globalObject, JSCell* base) const
340 {
341     ASSERT(base->structure() == this);
342     if (isObject())
343         return storedPrototype(asObject(base));
344     return prototypeForLookupPrimitiveImpl(globalObject, this);
345 }
346
347 inline StructureChain* Structure::cachedPrototypeChain() const
348 {
349     JSCell* cell = cachedPrototypeChainOrRareData();
350     if (isRareData(cell))
351         return jsCast<StructureRareData*>(cell)->cachedPrototypeChain();
352     return jsCast<StructureChain*>(cell);
353 }
354
355 inline void Structure::setCachedPrototypeChain(VM& vm, StructureChain* chain)
356 {
357     ASSERT(isObject());
358     ASSERT(!isCompilationThread() && !Thread::mayBeGCThread());
359     JSCell* cell = cachedPrototypeChainOrRareData();
360     if (isRareData(cell)) {
361         jsCast<StructureRareData*>(cell)->setCachedPrototypeChain(vm, chain);
362         return;
363     }
364 #if CPU(ADDRESS64)
365     m_inlineCapacityAndCachedPrototypeChainOrRareData.setPointer(chain);
366     vm.heap.writeBarrier(this, chain);
367 #else
368     m_cachedPrototypeChainOrRareData.setMayBeNull(vm, this, chain);
369 #endif
370 }
371
372 inline StructureChain* Structure::prototypeChain(VM& vm, JSGlobalObject* globalObject, JSObject* base) const
373 {
374     ASSERT(this->isObject());
375     ASSERT(base->structure(vm) == this);
376     // We cache our prototype chain so our clients can share it.
377     if (!isValid(globalObject, cachedPrototypeChain(), base)) {
378         JSValue prototype = prototypeForLookup(globalObject, base);
379         const_cast<Structure*>(this)->setCachedPrototypeChain(vm, StructureChain::create(vm, prototype.isNull() ? nullptr : asObject(prototype)));
380     }
381     return cachedPrototypeChain();
382 }
383
384 inline StructureChain* Structure::prototypeChain(JSGlobalObject* globalObject, JSObject* base) const
385 {
386     return prototypeChain(globalObject->vm(), globalObject, base);
387 }
388
389 inline bool Structure::isValid(JSGlobalObject* globalObject, StructureChain* cachedPrototypeChain, JSObject* base) const
390 {
391     if (!cachedPrototypeChain)
392         return false;
393
394     VM& vm = globalObject->vm();
395     JSValue prototype = prototypeForLookup(globalObject, base);
396     StructureID* cachedStructure = cachedPrototypeChain->head();
397     while (*cachedStructure && !prototype.isNull()) {
398         if (asObject(prototype)->structureID() != *cachedStructure)
399             return false;
400         ++cachedStructure;
401         prototype = asObject(prototype)->getPrototypeDirect(vm);
402     }
403     return prototype.isNull() && !*cachedStructure;
404 }
405
406 inline void Structure::didReplaceProperty(PropertyOffset offset)
407 {
408     if (LIKELY(!hasRareData()))
409         return;
410     StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get();
411     if (LIKELY(!map))
412         return;
413     WatchpointSet* set = map->get(offset);
414     if (LIKELY(!set))
415         return;
416     set->fireAll(vm(), "Property did get replaced");
417 }
418
419 inline WatchpointSet* Structure::propertyReplacementWatchpointSet(PropertyOffset offset)
420 {
421     ConcurrentJSCellLocker locker(cellLock());
422     if (!hasRareData())
423         return nullptr;
424     WTF::loadLoadFence();
425     StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get();
426     if (!map)
427         return nullptr;
428     return map->get(offset);
429 }
430
431 template<typename DetailsFunc>
432 ALWAYS_INLINE bool Structure::checkOffsetConsistency(PropertyTable* propertyTable, const DetailsFunc& detailsFunc) const
433 {
434     // We cannot reliably assert things about the property table in the concurrent
435     // compilation thread. It is possible for the table to be stolen and then have
436     // things added to it, which leads to the offsets being all messed up. We could
437     // get around this by grabbing a lock here, but I think that would be overkill.
438     if (isCompilationThread())
439         return true;
440     
441     unsigned totalSize = propertyTable->propertyStorageSize();
442     unsigned inlineOverflowAccordingToTotalSize = totalSize < inlineCapacity() ? 0 : totalSize - inlineCapacity();
443
444     auto fail = [&] (const char* description) {
445         dataLog("Detected offset inconsistency: ", description, "!\n");
446         dataLog("this = ", RawPointer(this), "\n");
447         dataLog("transitionOffset = ", transitionOffset(), "\n");
448         dataLog("maxOffset = ", maxOffset(), "\n");
449         dataLog("m_inlineCapacity = ", inlineCapacity(), "\n");
450         dataLog("propertyTable = ", RawPointer(propertyTable), "\n");
451         dataLog("numberOfSlotsForMaxOffset = ", numberOfSlotsForMaxOffset(maxOffset(), inlineCapacity()), "\n");
452         dataLog("totalSize = ", totalSize, "\n");
453         dataLog("inlineOverflowAccordingToTotalSize = ", inlineOverflowAccordingToTotalSize, "\n");
454         dataLog("numberOfOutOfLineSlotsForMaxOffset = ", numberOfOutOfLineSlotsForMaxOffset(maxOffset()), "\n");
455         detailsFunc();
456         UNREACHABLE_FOR_PLATFORM();
457     };
458     
459     if (numberOfSlotsForMaxOffset(maxOffset(), inlineCapacity()) != totalSize)
460         fail("numberOfSlotsForMaxOffset doesn't match totalSize");
461     if (inlineOverflowAccordingToTotalSize != numberOfOutOfLineSlotsForMaxOffset(maxOffset()))
462         fail("inlineOverflowAccordingToTotalSize doesn't match numberOfOutOfLineSlotsForMaxOffset");
463
464     return true;
465 }
466
467 ALWAYS_INLINE bool Structure::checkOffsetConsistency() const
468 {
469     PropertyTable* propertyTable = propertyTableUnsafeOrNull();
470
471     if (!propertyTable) {
472         ASSERT(!isPinnedPropertyTable());
473         return true;
474     }
475
476     // We cannot reliably assert things about the property table in the concurrent
477     // compilation thread. It is possible for the table to be stolen and then have
478     // things added to it, which leads to the offsets being all messed up. We could
479     // get around this by grabbing a lock here, but I think that would be overkill.
480     if (isCompilationThread())
481         return true;
482
483     return checkOffsetConsistency(propertyTable, [] () { });
484 }
485
486 inline void Structure::checkConsistency()
487 {
488     checkOffsetConsistency();
489 }
490
491 inline size_t nextOutOfLineStorageCapacity(size_t currentCapacity)
492 {
493     if (!currentCapacity)
494         return initialOutOfLineCapacity;
495     return currentCapacity * outOfLineGrowthFactor;
496 }
497
498 inline void Structure::setObjectToStringValue(JSGlobalObject* globalObject, VM& vm, JSString* value, PropertySlot toStringTagSymbolSlot)
499 {
500     if (!hasRareData())
501         allocateRareData(vm);
502     rareData()->setObjectToStringValue(globalObject, vm, this, value, toStringTagSymbolSlot);
503 }
504
505 template<Structure::ShouldPin shouldPin, typename Func>
506 inline PropertyOffset Structure::add(VM& vm, PropertyName propertyName, unsigned attributes, const Func& func)
507 {
508     PropertyTable* table = ensurePropertyTable(vm);
509
510     GCSafeConcurrentJSCellLocker locker(cellLock(), vm.heap);
511
512     switch (shouldPin) {
513     case ShouldPin::Yes:
514         pin(locker, vm, table);
515         break;
516     case ShouldPin::No:
517         setPropertyTable(vm, table);
518         break;
519     }
520     
521     ASSERT(!JSC::isValidOffset(get(vm, propertyName)));
522
523     checkConsistency();
524     if (attributes & PropertyAttribute::DontEnum || propertyName.isSymbol())
525         setIsQuickPropertyAccessAllowedForEnumeration(false);
526     if (propertyName == vm.propertyNames->underscoreProto)
527         setHasUnderscoreProtoPropertyExcludingOriginalProto(true);
528
529     auto rep = propertyName.uid();
530
531     PropertyOffset newOffset = table->nextOffset(inlineCapacity());
532
533     addPropertyHashAndSeenProperty(rep->existingSymbolAwareHash(), rep);
534
535     auto result = table->add(PropertyMapEntry(rep, newOffset, attributes));
536     ASSERT_UNUSED(result, result.second);
537     ASSERT_UNUSED(result, result.first.first->offset == newOffset);
538     auto newMaxOffset = std::max(newOffset, maxOffset());
539     
540     func(locker, newOffset, newMaxOffset);
541     
542     ASSERT(maxOffset() == newMaxOffset);
543
544     checkConsistency();
545     return newOffset;
546 }
547
548 template<Structure::ShouldPin shouldPin, typename Func>
549 inline PropertyOffset Structure::remove(VM& vm, PropertyName propertyName, const Func& func)
550 {
551     PropertyTable* table = ensurePropertyTable(vm);
552     GCSafeConcurrentJSCellLocker locker(cellLock(), vm.heap);
553
554     switch (shouldPin) {
555     case ShouldPin::Yes:
556         pin(locker, vm, table);
557         break;
558     case ShouldPin::No:
559         setPropertyTable(vm, table);
560         break;
561     }
562
563     ASSERT(JSC::isValidOffset(get(vm, propertyName)));
564
565     checkConsistency();
566
567     auto rep = propertyName.uid();
568
569     PropertyTable::find_iterator position = table->find(rep);
570     if (!position.first)
571         return invalidOffset;
572
573     setIsQuickPropertyAccessAllowedForEnumeration(false);
574     
575     PropertyOffset offset = position.first->offset;
576
577     table->remove(position);
578     table->addDeletedOffset(offset);
579
580     PropertyOffset newMaxOffset = maxOffset();
581
582     func(locker, offset, newMaxOffset);
583
584     ASSERT(maxOffset() == newMaxOffset);
585     ASSERT(!JSC::isValidOffset(get(vm, propertyName)));
586
587     checkConsistency();
588     return offset;
589 }
590
591 template<typename Func>
592 inline PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes, const Func& func)
593 {
594     return add<ShouldPin::Yes>(vm, propertyName, attributes, func);
595 }
596
597 template<typename Func>
598 inline PropertyOffset Structure::removePropertyWithoutTransition(VM& vm, PropertyName propertyName, const Func& func)
599 {
600     ASSERT(isUncacheableDictionary());
601     ASSERT(isPinnedPropertyTable());
602     ASSERT(propertyTableUnsafeOrNull());
603     
604     return remove<ShouldPin::Yes>(vm, propertyName, func);
605 }
606
607 ALWAYS_INLINE void Structure::setPrototypeWithoutTransition(VM& vm, JSValue prototype)
608 {
609     ASSERT(isValidPrototype(prototype));
610     m_prototype.set(vm, this, prototype);
611 }
612
613 ALWAYS_INLINE void Structure::setGlobalObject(VM& vm, JSGlobalObject* globalObject)
614 {
615     m_globalObject.set(vm, this, globalObject);
616 }
617
618 ALWAYS_INLINE void Structure::setPropertyTable(VM& vm, PropertyTable* table)
619 {
620 #if CPU(ADDRESS64)
621     m_outOfLineTypeFlagsAndPropertyTableUnsafe.setPointer(table);
622     vm.heap.writeBarrier(this, table);
623 #else
624     m_propertyTableUnsafe.setMayBeNull(vm, this, table);
625 #endif
626 }
627
628 ALWAYS_INLINE void Structure::clearPropertyTable()
629 {
630 #if CPU(ADDRESS64)
631     m_outOfLineTypeFlagsAndPropertyTableUnsafe.setPointer(nullptr);
632 #else
633     m_propertyTableUnsafe.clear();
634 #endif
635 }
636
637 ALWAYS_INLINE void Structure::setOutOfLineTypeFlags(TypeInfo::OutOfLineTypeFlags outOfLineTypeFlags)
638 {
639 #if CPU(ADDRESS64)
640     m_outOfLineTypeFlagsAndPropertyTableUnsafe.setType(outOfLineTypeFlags);
641 #else
642     m_outOfLineTypeFlags = outOfLineTypeFlags;
643 #endif
644 }
645
646 ALWAYS_INLINE void Structure::setInlineCapacity(uint8_t inlineCapacity)
647 {
648 #if CPU(ADDRESS64)
649     m_inlineCapacityAndCachedPrototypeChainOrRareData.setType(inlineCapacity);
650 #else
651     m_inlineCapacity = inlineCapacity;
652 #endif
653 }
654
655 ALWAYS_INLINE void Structure::setClassInfo(const ClassInfo* classInfo)
656 {
657 #if CPU(ADDRESS64)
658     m_transitionOffsetAndClassInfo.setPointer(classInfo);
659 #else
660     m_classInfo = classInfo;
661 #endif
662 }
663
664 ALWAYS_INLINE void Structure::setPreviousID(VM& vm, Structure* structure)
665 {
666     ASSERT(structure);
667     m_previousID = structure->id();
668     vm.heap.writeBarrier(this, structure);
669 }
670
671 inline void Structure::clearPreviousID()
672 {
673     m_previousID = 0;
674 }
675
676 ALWAYS_INLINE bool Structure::shouldConvertToPolyProto(const Structure* a, const Structure* b)
677 {
678     if (!a || !b)
679         return false;
680
681     if (a == b)
682         return false;
683
684     if (a->propertyHash() != b->propertyHash())
685         return false;
686
687     // We only care about objects created via a constructor's to_this. These
688     // all have Structures with rare data and a sharedPolyProtoWatchpoint.
689     if (!a->hasRareData() || !b->hasRareData())
690         return false;
691
692     // We only care about Structure's generated from functions that share
693     // the same executable.
694     const Box<InlineWatchpointSet>& aInlineWatchpointSet = a->rareData()->sharedPolyProtoWatchpoint();
695     const Box<InlineWatchpointSet>& bInlineWatchpointSet = b->rareData()->sharedPolyProtoWatchpoint();
696     if (aInlineWatchpointSet.get() != bInlineWatchpointSet.get() || !aInlineWatchpointSet)
697         return false;
698     ASSERT(aInlineWatchpointSet && bInlineWatchpointSet && aInlineWatchpointSet.get() == bInlineWatchpointSet.get());
699
700     if (a->hasPolyProto() || b->hasPolyProto())
701         return false;
702
703     if (a->storedPrototype() == b->storedPrototype())
704         return false;
705
706     VM& vm = a->vm();
707     JSObject* aObj = a->storedPrototypeObject();
708     JSObject* bObj = b->storedPrototypeObject();
709     while (aObj && bObj) {
710         a = aObj->structure(vm);
711         b = bObj->structure(vm);
712
713         if (a->propertyHash() != b->propertyHash())
714             return false;
715
716         aObj = a->storedPrototypeObject(aObj);
717         bObj = b->storedPrototypeObject(bObj);
718     }
719
720     return !aObj && !bObj;
721 }
722
723 inline Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPropertyTransition transitionKind)
724 {
725     IndexingType indexingModeIncludingHistory = newIndexingType(structure->indexingModeIncludingHistory(), transitionKind);
726
727     if (changesIndexingType(transitionKind)) {
728         if (JSGlobalObject* globalObject = structure->m_globalObject.get()) {
729             if (globalObject->isOriginalArrayStructure(structure)) {
730                 Structure* result = globalObject->originalArrayStructureForIndexingType(indexingModeIncludingHistory);
731                 if (result->indexingModeIncludingHistory() == indexingModeIncludingHistory) {
732                     structure->didTransitionFromThisStructure();
733                     return result;
734                 }
735             }
736         }
737     }
738
739     return nonPropertyTransitionSlow(vm, structure, transitionKind);
740 }
741
742 } // namespace JSC