2 * Copyright (C) 2008, 2009, 2012, 2013, 2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include "ClassInfo.h"
30 #include "ConcurrentJITLock.h"
31 #include "IndexingType.h"
32 #include "JSCJSValue.h"
35 #include "PropertyName.h"
36 #include "PropertyNameArray.h"
37 #include "PropertyOffset.h"
39 #include "PutPropertySlot.h"
40 #include "StructureIDBlob.h"
41 #include "StructureRareData.h"
42 #include "StructureTransitionTable.h"
43 #include "JSTypeInfo.h"
44 #include "Watchpoint.h"
46 #include "WriteBarrierInlines.h"
47 #include <wtf/CompilationThread.h>
48 #include <wtf/PassRefPtr.h>
49 #include <wtf/PrintStream.h>
50 #include <wtf/RefCounted.h>
51 #include <wtf/text/AtomicStringImpl.h>
56 class LLIntOffsetsExtractor;
57 class PropertyNameArray;
58 class PropertyNameArrayData;
66 // The out-of-line property storage capacity to use when first allocating out-of-line
67 // storage. Note that all objects start out without having any out-of-line storage;
68 // this comes into play only on the first property store that exhausts inline storage.
69 static const unsigned initialOutOfLineCapacity = 4;
71 // The factor by which to grow out-of-line storage when it is exhausted, after the
72 // initial allocation.
73 static const unsigned outOfLineGrowthFactor = 2;
75 struct PropertyMapEntry {
76 AtomicStringImpl* key;
77 PropertyOffset offset;
82 , offset(invalidOffset)
87 PropertyMapEntry(AtomicStringImpl* key, PropertyOffset offset, unsigned attributes)
90 , attributes(attributes)
95 class Structure final : public JSCell {
97 friend class StructureTransitionTable;
100 static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
102 static Structure* create(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0);
107 void finishCreation(VM& vm)
109 Base::finishCreation(vm);
111 ASSERT(m_prototype.isObject() || m_prototype.isNull());
114 void finishCreation(VM& vm, CreatingEarlyCellTag)
116 Base::finishCreation(vm, this, CreatingEarlyCell);
118 ASSERT(m_prototype.isNull());
119 ASSERT(!vm.structureStructure);
123 StructureID id() const { return m_blob.structureID(); }
124 int32_t objectInitializationBlob() const { return m_blob.blobExcludingStructureID(); }
125 int64_t idBlob() const { return m_blob.blob(); }
129 JSType type = m_blob.type();
130 return type == ImpureProxyType || type == PureForwardingProxyType;
133 static void dumpStatistics();
135 JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext);
136 static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, AtomicStringImpl* uid, unsigned attributes, PropertyOffset&);
137 JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, PropertyOffset&);
138 static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&);
139 JS_EXPORT_PRIVATE static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype);
140 JS_EXPORT_PRIVATE static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes);
141 JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*);
142 static Structure* toUncacheableDictionaryTransition(VM&, Structure*);
143 JS_EXPORT_PRIVATE static Structure* sealTransition(VM&, Structure*);
144 JS_EXPORT_PRIVATE static Structure* freezeTransition(VM&, Structure*);
145 static Structure* preventExtensionsTransition(VM&, Structure*);
146 JS_EXPORT_PRIVATE static Structure* nonPropertyTransition(VM&, Structure*, NonPropertyTransition);
148 JS_EXPORT_PRIVATE bool isSealed(VM&);
149 JS_EXPORT_PRIVATE bool isFrozen(VM&);
150 bool isExtensible() const { return !preventExtensions(); }
151 bool putWillGrowOutOfLineStorage();
152 size_t suggestedNewOutOfLineStorageCapacity();
154 JS_EXPORT_PRIVATE Structure* flattenDictionaryStructure(VM&, JSObject*);
156 static const bool needsDestruction = true;
157 static void destroy(JSCell*);
159 // These should be used with caution.
160 JS_EXPORT_PRIVATE PropertyOffset addPropertyWithoutTransition(VM&, PropertyName, unsigned attributes);
161 PropertyOffset removePropertyWithoutTransition(VM&, PropertyName);
162 void setPrototypeWithoutTransition(VM& vm, JSValue prototype) { m_prototype.set(vm, this, prototype); }
164 bool isDictionary() const { return dictionaryKind() != NoneDictionaryKind; }
165 bool isUncacheableDictionary() const { return dictionaryKind() == UncachedDictionaryKind; }
167 bool propertyAccessesAreCacheable() { return dictionaryKind() != UncachedDictionaryKind && !typeInfo().prohibitsPropertyCaching(); }
169 // We use SlowPath in GetByIdStatus for structures that may get new impure properties later to prevent
170 // DFG from inlining property accesses since structures don't transition when a new impure property appears.
171 bool takesSlowPathInDFGForImpureProperty()
173 return typeInfo().hasImpureGetOwnPropertySlot();
177 TypeInfo typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_blob.typeInfo(m_outOfLineTypeFlags); }
178 bool isObject() const { return typeInfo().isObject(); }
180 IndexingType indexingType() const { return m_blob.indexingType() & AllArrayTypes; }
181 IndexingType indexingTypeIncludingHistory() const { return m_blob.indexingType(); }
183 bool mayInterceptIndexedAccesses() const
185 return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors);
188 bool anyObjectInChainMayInterceptIndexedAccesses() const;
189 bool holesMustForwardToPrototype(VM&) const;
191 bool needsSlowPutIndexing() const;
192 NonPropertyTransition suggestedArrayStorageTransition() const;
194 JSGlobalObject* globalObject() const { return m_globalObject.get(); }
195 void setGlobalObject(VM& vm, JSGlobalObject* globalObject) { m_globalObject.set(vm, this, globalObject); }
197 JSValue storedPrototype() const { return m_prototype.get(); }
198 JSObject* storedPrototypeObject() const;
199 Structure* storedPrototypeStructure() const;
200 JSValue prototypeForLookup(ExecState*) const;
201 JSValue prototypeForLookup(JSGlobalObject*) const;
202 JSValue prototypeForLookup(CodeBlock*) const;
203 StructureChain* prototypeChain(VM&, JSGlobalObject*) const;
204 StructureChain* prototypeChain(ExecState*) const;
205 static void visitChildren(JSCell*, SlotVisitor&);
207 // Will just the prototype chain intercept this property access?
208 bool prototypeChainMayInterceptStoreTo(VM&, PropertyName);
210 Structure* previousID() const
212 ASSERT(structure()->classInfo() == info());
214 return rareData()->previousID();
217 bool transitivelyTransitionedFrom(Structure* structureToFind);
219 unsigned outOfLineCapacity() const
221 ASSERT(checkOffsetConsistency());
223 unsigned outOfLineSize = this->outOfLineSize();
228 if (outOfLineSize <= initialOutOfLineCapacity)
229 return initialOutOfLineCapacity;
231 ASSERT(outOfLineSize > initialOutOfLineCapacity);
232 COMPILE_ASSERT(outOfLineGrowthFactor == 2, outOfLineGrowthFactor_is_two);
233 return WTF::roundUpToPowerOfTwo(outOfLineSize);
235 unsigned outOfLineSize() const
237 ASSERT(checkOffsetConsistency());
238 ASSERT(structure()->classInfo() == info());
240 return numberOfOutOfLineSlotsForLastOffset(m_offset);
242 bool hasInlineStorage() const
244 return !!m_inlineCapacity;
246 unsigned inlineCapacity() const
248 return m_inlineCapacity;
250 unsigned inlineSize() const
252 return std::min<unsigned>(m_offset + 1, m_inlineCapacity);
254 unsigned totalStorageSize() const
256 return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
258 unsigned totalStorageCapacity() const
260 ASSERT(structure()->classInfo() == info());
261 return outOfLineCapacity() + inlineCapacity();
264 bool isValidOffset(PropertyOffset offset) const
266 return JSC::isValidOffset(offset)
267 && offset <= m_offset
268 && (offset < m_inlineCapacity || offset >= firstOutOfLineOffset);
271 bool couldHaveIndexingHeader() const
273 return hasIndexedProperties(indexingType())
274 || isTypedView(m_classInfo->typedArrayStorageType);
277 bool hasIndexingHeader(const JSCell*) const;
279 bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject);
281 PropertyOffset get(VM&, PropertyName);
282 PropertyOffset get(VM&, PropertyName, unsigned& attributes);
284 // This is a somewhat internalish method. It will call your functor while possibly holding the
285 // Structure's lock. There is no guarantee whether the lock is held or not in any particular
286 // call. So, you have to assume the worst. Also, the functor returns true if it wishes for you
287 // to continue or false if it's done.
288 template<typename Functor>
289 void forEachPropertyConcurrently(const Functor&);
291 PropertyOffset getConcurrently(AtomicStringImpl* uid);
292 PropertyOffset getConcurrently(AtomicStringImpl* uid, unsigned& attributes);
294 Vector<PropertyMapEntry> getPropertiesConcurrently();
296 void setHasGetterSetterPropertiesWithProtoCheck(bool is__proto__)
298 setHasGetterSetterProperties(true);
300 setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
303 void setContainsReadOnlyProperties() { setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); }
305 void setHasCustomGetterSetterPropertiesWithProtoCheck(bool is__proto__)
307 setHasCustomGetterSetterProperties(true);
309 setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
314 ASSERT(checkOffsetConsistency());
315 return !JSC::isValidOffset(m_offset);
318 void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*);
319 JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const;
320 bool canCachePropertyNameEnumerator() const;
321 bool canAccessPropertiesQuickly() const;
323 void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
325 JSString* objectToStringValue()
329 return rareData()->objectToStringValue();
332 void setObjectToStringValue(VM& vm, JSString* value)
335 allocateRareData(vm);
336 rareData()->setObjectToStringValue(vm, value);
339 const ClassInfo* classInfo() const { return m_classInfo; }
341 static ptrdiff_t structureIDOffset()
343 return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset();
346 static ptrdiff_t prototypeOffset()
348 return OBJECT_OFFSETOF(Structure, m_prototype);
351 static ptrdiff_t globalObjectOffset()
353 return OBJECT_OFFSETOF(Structure, m_globalObject);
356 static ptrdiff_t classInfoOffset()
358 return OBJECT_OFFSETOF(Structure, m_classInfo);
361 static ptrdiff_t indexingTypeOffset()
363 return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingTypeOffset();
366 static Structure* createStructure(VM&);
368 bool transitionWatchpointSetHasBeenInvalidated() const
370 return m_transitionWatchpointSet.hasBeenInvalidated();
373 bool transitionWatchpointSetIsStillValid() const
375 return m_transitionWatchpointSet.isStillValid();
378 bool dfgShouldWatchIfPossible() const
380 // FIXME: We would like to not watch things that are unprofitable to watch, like
381 // dictionaries. Unfortunately, we can't do such things: a dictionary could get flattened,
382 // in which case it will start to appear watchable and so the DFG will think that it is
383 // watching it. We should come up with a comprehensive story for not watching things that
384 // aren't profitable to watch.
385 // https://bugs.webkit.org/show_bug.cgi?id=133625
389 bool dfgShouldWatch() const
391 return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid();
394 void addTransitionWatchpoint(Watchpoint* watchpoint) const
396 ASSERT(transitionWatchpointSetIsStillValid());
397 m_transitionWatchpointSet.add(watchpoint);
400 void didTransitionFromThisStructure() const;
402 InlineWatchpointSet& transitionWatchpointSet() const
404 return m_transitionWatchpointSet;
407 WatchpointSet* ensurePropertyReplacementWatchpointSet(VM&, PropertyOffset);
408 void startWatchingPropertyForReplacements(VM& vm, PropertyOffset offset)
410 ensurePropertyReplacementWatchpointSet(vm, offset);
412 void startWatchingPropertyForReplacements(VM&, PropertyName);
413 WatchpointSet* propertyReplacementWatchpointSet(PropertyOffset);
414 void didReplaceProperty(PropertyOffset);
415 void didCachePropertyReplacement(VM&, PropertyOffset);
417 void startWatchingInternalPropertiesIfNecessary(VM& vm)
419 if (LIKELY(didWatchInternalProperties()))
421 startWatchingInternalProperties(vm);
424 void startWatchingInternalPropertiesIfNecessaryForEntireChain(VM& vm)
426 for (Structure* structure = this; structure; structure = structure->storedPrototypeStructure())
427 structure->startWatchingInternalPropertiesIfNecessary(vm);
430 PassRefPtr<StructureShape> toStructureShape(JSValue);
432 // Determines if the two structures match enough that this one could be used for allocations
434 bool canUseForAllocationsOf(Structure*);
436 void dump(PrintStream&) const;
437 void dumpInContext(PrintStream&, DumpContext*) const;
438 void dumpBrief(PrintStream&, const CString&) const;
440 static void dumpContextHeader(PrintStream&);
446 NoneDictionaryKind = 0,
447 CachedDictionaryKind = 1,
448 UncachedDictionaryKind = 2
452 #define DEFINE_BITFIELD(type, lowerName, upperName, width, offset) \
453 static const uint32_t s_##lowerName##Shift = offset;\
454 static const uint32_t s_##lowerName##Mask = ((1 << (width - 1)) | ((1 << (width - 1)) - 1));\
455 type lowerName() const { return static_cast<type>((m_bitField >> offset) & s_##lowerName##Mask); }\
456 void set##upperName(type newValue) \
458 m_bitField &= ~(s_##lowerName##Mask << offset);\
459 m_bitField |= (newValue & s_##lowerName##Mask) << offset;\
462 DEFINE_BITFIELD(DictionaryKind, dictionaryKind, DictionaryKind, 2, 0);
463 DEFINE_BITFIELD(bool, isPinnedPropertyTable, IsPinnedPropertyTable, 1, 2);
464 DEFINE_BITFIELD(bool, hasGetterSetterProperties, HasGetterSetterProperties, 1, 3);
465 DEFINE_BITFIELD(bool, hasReadOnlyOrGetterSetterPropertiesExcludingProto, HasReadOnlyOrGetterSetterPropertiesExcludingProto, 1, 4);
466 DEFINE_BITFIELD(bool, hasNonEnumerableProperties, HasNonEnumerableProperties, 1, 5);
467 DEFINE_BITFIELD(unsigned, attributesInPrevious, AttributesInPrevious, 14, 6);
468 DEFINE_BITFIELD(bool, preventExtensions, PreventExtensions, 1, 20);
469 DEFINE_BITFIELD(bool, didTransition, DidTransition, 1, 21);
470 DEFINE_BITFIELD(bool, staticFunctionsReified, StaticFunctionsReified, 1, 22);
471 DEFINE_BITFIELD(bool, hasRareData, HasRareData, 1, 23);
472 DEFINE_BITFIELD(bool, hasBeenFlattenedBefore, HasBeenFlattenedBefore, 1, 24);
473 DEFINE_BITFIELD(bool, hasCustomGetterSetterProperties, HasCustomGetterSetterProperties, 1, 25);
474 DEFINE_BITFIELD(bool, didWatchInternalProperties, DidWatchInternalProperties, 1, 26);
477 friend class LLIntOffsetsExtractor;
479 JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity);
481 Structure(VM&, Structure*);
483 static Structure* create(VM&, Structure*);
485 static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, AtomicStringImpl* uid, unsigned attributes, PropertyOffset&);
487 // This will return the structure that has a usable property table, that property table,
488 // and the list of structures that we visited before we got to it. If it returns a
489 // non-null structure, it will also lock the structure that it returns; it is your job
491 void findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*&, PropertyTable*&);
493 static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind);
495 PropertyOffset add(VM&, PropertyName, unsigned attributes);
496 PropertyOffset remove(PropertyName);
498 void createPropertyMap(const GCSafeConcurrentJITLocker&, VM&, unsigned keyCount = 0);
499 void checkConsistency();
501 WriteBarrier<PropertyTable>& propertyTable();
502 PropertyTable* takePropertyTableOrCloneIfPinned(VM&);
503 PropertyTable* copyPropertyTable(VM&);
504 PropertyTable* copyPropertyTableForPinning(VM&);
505 JS_EXPORT_PRIVATE void materializePropertyMap(VM&);
506 ALWAYS_INLINE void materializePropertyMapIfNecessary(VM& vm, DeferGC&)
508 ASSERT(!isCompilationThread());
509 ASSERT(structure()->classInfo() == info());
510 ASSERT(checkOffsetConsistency());
511 if (!propertyTable() && previousID())
512 materializePropertyMap(vm);
514 ALWAYS_INLINE void materializePropertyMapIfNecessary(VM& vm, PropertyTable*& table)
516 ASSERT(!isCompilationThread());
517 ASSERT(structure()->classInfo() == info());
518 ASSERT(checkOffsetConsistency());
519 table = propertyTable().get();
520 if (!table && previousID()) {
521 DeferGC deferGC(vm.heap);
522 materializePropertyMap(vm);
523 table = propertyTable().get();
526 void materializePropertyMapIfNecessaryForPinning(VM& vm, DeferGC&)
528 ASSERT(structure()->classInfo() == info());
529 checkOffsetConsistency();
530 if (!propertyTable())
531 materializePropertyMap(vm);
534 void setPreviousID(VM& vm, Structure* structure)
537 rareData()->setPreviousID(vm, structure);
539 m_previousOrRareData.set(vm, this, structure);
542 void clearPreviousID()
545 rareData()->clearPreviousID();
547 m_previousOrRareData.clear();
550 int transitionCount() const
552 // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
553 return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
556 bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const;
557 bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
561 Structure* previous() const
563 ASSERT(!hasRareData());
564 return static_cast<Structure*>(m_previousOrRareData.get());
567 StructureRareData* rareData() const
569 ASSERT(hasRareData());
570 return static_cast<StructureRareData*>(m_previousOrRareData.get());
573 bool checkOffsetConsistency() const;
575 JS_EXPORT_PRIVATE void allocateRareData(VM&);
577 void startWatchingInternalProperties(VM&);
579 static const int s_maxTransitionLength = 64;
580 static const int s_maxTransitionLengthForNonEvalPutById = 512;
582 // These need to be properly aligned at the beginning of the 'Structure'
583 // part of the object.
584 StructureIDBlob m_blob;
585 TypeInfo::OutOfLineTypeFlags m_outOfLineTypeFlags;
587 WriteBarrier<JSGlobalObject> m_globalObject;
588 WriteBarrier<Unknown> m_prototype;
589 mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
591 WriteBarrier<JSCell> m_previousOrRareData;
593 RefPtr<AtomicStringImpl> m_nameInPrevious;
595 const ClassInfo* m_classInfo;
597 StructureTransitionTable m_transitionTable;
599 // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread.
600 WriteBarrier<PropertyTable> m_propertyTableUnsafe;
602 mutable InlineWatchpointSet m_transitionWatchpointSet;
604 COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits);
606 // m_offset does not account for anonymous slots
607 PropertyOffset m_offset;
609 uint8_t m_inlineCapacity;
611 ConcurrentJITLock m_lock;
618 #endif // Structure_h