[JSC] Delete IC creation should check mayNeedToCheckCell/canCacheDeleteIC regardless...
[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() const
260 {
261     if (hasIndexedProperties(indexingType()))
262         return true;
263
264     if (!isTypedView(typedArrayTypeForType(m_blob.type())))
265         return false;
266
267     return true;
268 }
269
270 inline bool Structure::canCacheDeleteIC() const
271 {
272     return !isTypedView(typedArrayTypeForType(m_blob.type()));
273 }
274
275 inline bool Structure::masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject)
276 {
277     return typeInfo().masqueradesAsUndefined() && globalObject() == lexicalGlobalObject;
278 }
279
280 inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind)
281 {
282     VM& vm = this->vm();
283     for (Structure* current = this; current; current = current->previousID(vm)) {
284         if (current == structureToFind)
285             return true;
286     }
287     return false;
288 }
289
290 inline void Structure::setCachedOwnKeys(VM& vm, JSImmutableButterfly* ownKeys)
291 {
292     ensureRareData(vm)->setCachedOwnKeys(vm, ownKeys);
293 }
294
295 inline JSImmutableButterfly* Structure::cachedOwnKeys() const
296 {
297     if (!hasRareData())
298         return nullptr;
299     return rareData()->cachedOwnKeys();
300 }
301
302 inline JSImmutableButterfly* Structure::cachedOwnKeysIgnoringSentinel() const
303 {
304     if (!hasRareData())
305         return nullptr;
306     return rareData()->cachedOwnKeysIgnoringSentinel();
307 }
308
309 inline bool Structure::canCacheOwnKeys() const
310 {
311     if (isDictionary())
312         return false;
313     if (hasIndexedProperties(indexingType()))
314         return false;
315     if (typeInfo().overridesGetPropertyNames())
316         return false;
317     return true;
318 }
319
320 ALWAYS_INLINE JSValue prototypeForLookupPrimitiveImpl(JSGlobalObject* globalObject, const Structure* structure)
321 {
322     ASSERT(!structure->isObject());
323
324     if (structure->typeInfo().type() == StringType)
325         return globalObject->stringPrototype();
326     
327     if (structure->typeInfo().type() == BigIntType)
328         return globalObject->bigIntPrototype();
329
330     ASSERT(structure->typeInfo().type() == SymbolType);
331     return globalObject->symbolPrototype();
332 }
333
334 inline JSValue Structure::prototypeForLookup(JSGlobalObject* globalObject) const
335 {
336     ASSERT(hasMonoProto());
337     if (isObject())
338         return storedPrototype();
339     return prototypeForLookupPrimitiveImpl(globalObject, this);
340 }
341
342 inline JSValue Structure::prototypeForLookup(JSGlobalObject* globalObject, JSCell* base) const
343 {
344     ASSERT(base->structure() == this);
345     if (isObject())
346         return storedPrototype(asObject(base));
347     return prototypeForLookupPrimitiveImpl(globalObject, this);
348 }
349
350 inline StructureChain* Structure::cachedPrototypeChain() const
351 {
352     JSCell* cell = cachedPrototypeChainOrRareData();
353     if (isRareData(cell))
354         return jsCast<StructureRareData*>(cell)->cachedPrototypeChain();
355     return jsCast<StructureChain*>(cell);
356 }
357
358 inline void Structure::setCachedPrototypeChain(VM& vm, StructureChain* chain)
359 {
360     ASSERT(isObject());
361     ASSERT(!isCompilationThread() && !Thread::mayBeGCThread());
362     JSCell* cell = cachedPrototypeChainOrRareData();
363     if (isRareData(cell)) {
364         jsCast<StructureRareData*>(cell)->setCachedPrototypeChain(vm, chain);
365         return;
366     }
367 #if CPU(ADDRESS64)
368     m_inlineCapacityAndCachedPrototypeChainOrRareData.setPointer(chain);
369     vm.heap.writeBarrier(this, chain);
370 #else
371     m_cachedPrototypeChainOrRareData.setMayBeNull(vm, this, chain);
372 #endif
373 }
374
375 inline StructureChain* Structure::prototypeChain(VM& vm, JSGlobalObject* globalObject, JSObject* base) const
376 {
377     ASSERT(this->isObject());
378     ASSERT(base->structure(vm) == this);
379     // We cache our prototype chain so our clients can share it.
380     if (!isValid(globalObject, cachedPrototypeChain(), base)) {
381         JSValue prototype = prototypeForLookup(globalObject, base);
382         const_cast<Structure*>(this)->setCachedPrototypeChain(vm, StructureChain::create(vm, prototype.isNull() ? nullptr : asObject(prototype)));
383     }
384     return cachedPrototypeChain();
385 }
386
387 inline StructureChain* Structure::prototypeChain(JSGlobalObject* globalObject, JSObject* base) const
388 {
389     return prototypeChain(globalObject->vm(), globalObject, base);
390 }
391
392 inline bool Structure::isValid(JSGlobalObject* globalObject, StructureChain* cachedPrototypeChain, JSObject* base) const
393 {
394     if (!cachedPrototypeChain)
395         return false;
396
397     VM& vm = globalObject->vm();
398     JSValue prototype = prototypeForLookup(globalObject, base);
399     StructureID* cachedStructure = cachedPrototypeChain->head();
400     while (*cachedStructure && !prototype.isNull()) {
401         if (asObject(prototype)->structureID() != *cachedStructure)
402             return false;
403         ++cachedStructure;
404         prototype = asObject(prototype)->getPrototypeDirect(vm);
405     }
406     return prototype.isNull() && !*cachedStructure;
407 }
408
409 inline void Structure::didReplaceProperty(PropertyOffset offset)
410 {
411     if (LIKELY(!hasRareData()))
412         return;
413     StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get();
414     if (LIKELY(!map))
415         return;
416     WatchpointSet* set = map->get(offset);
417     if (LIKELY(!set))
418         return;
419     set->fireAll(vm(), "Property did get replaced");
420 }
421
422 inline WatchpointSet* Structure::propertyReplacementWatchpointSet(PropertyOffset offset)
423 {
424     ConcurrentJSCellLocker locker(cellLock());
425     if (!hasRareData())
426         return nullptr;
427     WTF::loadLoadFence();
428     StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get();
429     if (!map)
430         return nullptr;
431     return map->get(offset);
432 }
433
434 template<typename DetailsFunc>
435 ALWAYS_INLINE bool Structure::checkOffsetConsistency(PropertyTable* propertyTable, const DetailsFunc& detailsFunc) const
436 {
437     // We cannot reliably assert things about the property table in the concurrent
438     // compilation thread. It is possible for the table to be stolen and then have
439     // things added to it, which leads to the offsets being all messed up. We could
440     // get around this by grabbing a lock here, but I think that would be overkill.
441     if (isCompilationThread())
442         return true;
443     
444     unsigned totalSize = propertyTable->propertyStorageSize();
445     unsigned inlineOverflowAccordingToTotalSize = totalSize < inlineCapacity() ? 0 : totalSize - inlineCapacity();
446
447     auto fail = [&] (const char* description) {
448         dataLog("Detected offset inconsistency: ", description, "!\n");
449         dataLog("this = ", RawPointer(this), "\n");
450         dataLog("transitionOffset = ", transitionOffset(), "\n");
451         dataLog("maxOffset = ", maxOffset(), "\n");
452         dataLog("m_inlineCapacity = ", inlineCapacity(), "\n");
453         dataLog("propertyTable = ", RawPointer(propertyTable), "\n");
454         dataLog("numberOfSlotsForMaxOffset = ", numberOfSlotsForMaxOffset(maxOffset(), inlineCapacity()), "\n");
455         dataLog("totalSize = ", totalSize, "\n");
456         dataLog("inlineOverflowAccordingToTotalSize = ", inlineOverflowAccordingToTotalSize, "\n");
457         dataLog("numberOfOutOfLineSlotsForMaxOffset = ", numberOfOutOfLineSlotsForMaxOffset(maxOffset()), "\n");
458         detailsFunc();
459         UNREACHABLE_FOR_PLATFORM();
460     };
461     
462     if (numberOfSlotsForMaxOffset(maxOffset(), inlineCapacity()) != totalSize)
463         fail("numberOfSlotsForMaxOffset doesn't match totalSize");
464     if (inlineOverflowAccordingToTotalSize != numberOfOutOfLineSlotsForMaxOffset(maxOffset()))
465         fail("inlineOverflowAccordingToTotalSize doesn't match numberOfOutOfLineSlotsForMaxOffset");
466
467     return true;
468 }
469
470 ALWAYS_INLINE bool Structure::checkOffsetConsistency() const
471 {
472     PropertyTable* propertyTable = propertyTableUnsafeOrNull();
473
474     if (!propertyTable) {
475         ASSERT(!isPinnedPropertyTable());
476         return true;
477     }
478
479     // We cannot reliably assert things about the property table in the concurrent
480     // compilation thread. It is possible for the table to be stolen and then have
481     // things added to it, which leads to the offsets being all messed up. We could
482     // get around this by grabbing a lock here, but I think that would be overkill.
483     if (isCompilationThread())
484         return true;
485
486     return checkOffsetConsistency(propertyTable, [] () { });
487 }
488
489 inline void Structure::checkConsistency()
490 {
491     checkOffsetConsistency();
492 }
493
494 inline size_t nextOutOfLineStorageCapacity(size_t currentCapacity)
495 {
496     if (!currentCapacity)
497         return initialOutOfLineCapacity;
498     return currentCapacity * outOfLineGrowthFactor;
499 }
500
501 inline void Structure::setObjectToStringValue(JSGlobalObject* globalObject, VM& vm, JSString* value, PropertySlot toStringTagSymbolSlot)
502 {
503     if (!hasRareData())
504         allocateRareData(vm);
505     rareData()->setObjectToStringValue(globalObject, vm, this, value, toStringTagSymbolSlot);
506 }
507
508 template<Structure::ShouldPin shouldPin, typename Func>
509 inline PropertyOffset Structure::add(VM& vm, PropertyName propertyName, unsigned attributes, const Func& func)
510 {
511     PropertyTable* table = ensurePropertyTable(vm);
512
513     GCSafeConcurrentJSCellLocker locker(cellLock(), vm.heap);
514
515     switch (shouldPin) {
516     case ShouldPin::Yes:
517         pin(locker, vm, table);
518         break;
519     case ShouldPin::No:
520         setPropertyTable(vm, table);
521         break;
522     }
523     
524     ASSERT(!JSC::isValidOffset(get(vm, propertyName)));
525
526     checkConsistency();
527     if (attributes & PropertyAttribute::DontEnum || propertyName.isSymbol())
528         setIsQuickPropertyAccessAllowedForEnumeration(false);
529     if (propertyName == vm.propertyNames->underscoreProto)
530         setHasUnderscoreProtoPropertyExcludingOriginalProto(true);
531
532     auto rep = propertyName.uid();
533
534     PropertyOffset newOffset = table->nextOffset(inlineCapacity());
535
536     addPropertyHashAndSeenProperty(rep->existingSymbolAwareHash(), rep);
537
538     auto result = table->add(PropertyMapEntry(rep, newOffset, attributes));
539     ASSERT_UNUSED(result, result.second);
540     ASSERT_UNUSED(result, result.first.first->offset == newOffset);
541     auto newMaxOffset = std::max(newOffset, maxOffset());
542     
543     func(locker, newOffset, newMaxOffset);
544     
545     ASSERT(maxOffset() == newMaxOffset);
546
547     checkConsistency();
548     return newOffset;
549 }
550
551 template<Structure::ShouldPin shouldPin, typename Func>
552 inline PropertyOffset Structure::remove(VM& vm, PropertyName propertyName, const Func& func)
553 {
554     PropertyTable* table = ensurePropertyTable(vm);
555     GCSafeConcurrentJSCellLocker locker(cellLock(), vm.heap);
556
557     switch (shouldPin) {
558     case ShouldPin::Yes:
559         pin(locker, vm, table);
560         break;
561     case ShouldPin::No:
562         setPropertyTable(vm, table);
563         break;
564     }
565
566     ASSERT(JSC::isValidOffset(get(vm, propertyName)));
567
568     checkConsistency();
569
570     auto rep = propertyName.uid();
571
572     PropertyTable::find_iterator position = table->find(rep);
573     if (!position.first)
574         return invalidOffset;
575
576     setIsQuickPropertyAccessAllowedForEnumeration(false);
577     
578     PropertyOffset offset = position.first->offset;
579
580     table->remove(position);
581     table->addDeletedOffset(offset);
582
583     PropertyOffset newMaxOffset = maxOffset();
584
585     func(locker, offset, newMaxOffset);
586
587     ASSERT(maxOffset() == newMaxOffset);
588     ASSERT(!JSC::isValidOffset(get(vm, propertyName)));
589
590     checkConsistency();
591     return offset;
592 }
593
594 template<typename Func>
595 inline PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes, const Func& func)
596 {
597     return add<ShouldPin::Yes>(vm, propertyName, attributes, func);
598 }
599
600 template<typename Func>
601 inline PropertyOffset Structure::removePropertyWithoutTransition(VM& vm, PropertyName propertyName, const Func& func)
602 {
603     ASSERT(isUncacheableDictionary());
604     ASSERT(isPinnedPropertyTable());
605     ASSERT(propertyTableUnsafeOrNull());
606     
607     return remove<ShouldPin::Yes>(vm, propertyName, func);
608 }
609
610 ALWAYS_INLINE void Structure::setPrototypeWithoutTransition(VM& vm, JSValue prototype)
611 {
612     ASSERT(isValidPrototype(prototype));
613     m_prototype.set(vm, this, prototype);
614 }
615
616 ALWAYS_INLINE void Structure::setGlobalObject(VM& vm, JSGlobalObject* globalObject)
617 {
618     m_globalObject.set(vm, this, globalObject);
619 }
620
621 ALWAYS_INLINE void Structure::setPropertyTable(VM& vm, PropertyTable* table)
622 {
623 #if CPU(ADDRESS64)
624     m_outOfLineTypeFlagsAndPropertyTableUnsafe.setPointer(table);
625     vm.heap.writeBarrier(this, table);
626 #else
627     m_propertyTableUnsafe.setMayBeNull(vm, this, table);
628 #endif
629 }
630
631 ALWAYS_INLINE void Structure::clearPropertyTable()
632 {
633 #if CPU(ADDRESS64)
634     m_outOfLineTypeFlagsAndPropertyTableUnsafe.setPointer(nullptr);
635 #else
636     m_propertyTableUnsafe.clear();
637 #endif
638 }
639
640 ALWAYS_INLINE void Structure::setOutOfLineTypeFlags(TypeInfo::OutOfLineTypeFlags outOfLineTypeFlags)
641 {
642 #if CPU(ADDRESS64)
643     m_outOfLineTypeFlagsAndPropertyTableUnsafe.setType(outOfLineTypeFlags);
644 #else
645     m_outOfLineTypeFlags = outOfLineTypeFlags;
646 #endif
647 }
648
649 ALWAYS_INLINE void Structure::setInlineCapacity(uint8_t inlineCapacity)
650 {
651 #if CPU(ADDRESS64)
652     m_inlineCapacityAndCachedPrototypeChainOrRareData.setType(inlineCapacity);
653 #else
654     m_inlineCapacity = inlineCapacity;
655 #endif
656 }
657
658 ALWAYS_INLINE void Structure::setClassInfo(const ClassInfo* classInfo)
659 {
660 #if CPU(ADDRESS64)
661     m_transitionOffsetAndClassInfo.setPointer(classInfo);
662 #else
663     m_classInfo = classInfo;
664 #endif
665 }
666
667 ALWAYS_INLINE void Structure::setPreviousID(VM& vm, Structure* structure)
668 {
669     ASSERT(structure);
670     m_previousID = structure->id();
671     vm.heap.writeBarrier(this, structure);
672 }
673
674 inline void Structure::clearPreviousID()
675 {
676     m_previousID = 0;
677 }
678
679 ALWAYS_INLINE bool Structure::shouldConvertToPolyProto(const Structure* a, const Structure* b)
680 {
681     if (!a || !b)
682         return false;
683
684     if (a == b)
685         return false;
686
687     if (a->propertyHash() != b->propertyHash())
688         return false;
689
690     // We only care about objects created via a constructor's to_this. These
691     // all have Structures with rare data and a sharedPolyProtoWatchpoint.
692     if (!a->hasRareData() || !b->hasRareData())
693         return false;
694
695     // We only care about Structure's generated from functions that share
696     // the same executable.
697     const Box<InlineWatchpointSet>& aInlineWatchpointSet = a->rareData()->sharedPolyProtoWatchpoint();
698     const Box<InlineWatchpointSet>& bInlineWatchpointSet = b->rareData()->sharedPolyProtoWatchpoint();
699     if (aInlineWatchpointSet.get() != bInlineWatchpointSet.get() || !aInlineWatchpointSet)
700         return false;
701     ASSERT(aInlineWatchpointSet && bInlineWatchpointSet && aInlineWatchpointSet.get() == bInlineWatchpointSet.get());
702
703     if (a->hasPolyProto() || b->hasPolyProto())
704         return false;
705
706     if (a->storedPrototype() == b->storedPrototype())
707         return false;
708
709     VM& vm = a->vm();
710     JSObject* aObj = a->storedPrototypeObject();
711     JSObject* bObj = b->storedPrototypeObject();
712     while (aObj && bObj) {
713         a = aObj->structure(vm);
714         b = bObj->structure(vm);
715
716         if (a->propertyHash() != b->propertyHash())
717             return false;
718
719         aObj = a->storedPrototypeObject(aObj);
720         bObj = b->storedPrototypeObject(bObj);
721     }
722
723     return !aObj && !bObj;
724 }
725
726 inline Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPropertyTransition transitionKind)
727 {
728     IndexingType indexingModeIncludingHistory = newIndexingType(structure->indexingModeIncludingHistory(), transitionKind);
729
730     if (changesIndexingType(transitionKind)) {
731         if (JSGlobalObject* globalObject = structure->m_globalObject.get()) {
732             if (globalObject->isOriginalArrayStructure(structure)) {
733                 Structure* result = globalObject->originalArrayStructureForIndexingType(indexingModeIncludingHistory);
734                 if (result->indexingModeIncludingHistory() == indexingModeIncludingHistory) {
735                     structure->didTransitionFromThisStructure();
736                     return result;
737                 }
738             }
739         }
740     }
741
742     return nonPropertyTransitionSlow(vm, structure, transitionKind);
743 }
744
745 } // namespace JSC