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>
54 class UniquedStringImpl
;
61 class LLIntOffsetsExtractor
;
62 class PropertyNameArray
;
63 class PropertyNameArrayData
;
71 // The out-of-line property storage capacity to use when first allocating out-of-line
72 // storage. Note that all objects start out without having any out-of-line storage;
73 // this comes into play only on the first property store that exhausts inline storage.
74 static const unsigned initialOutOfLineCapacity
= 4;
76 // The factor by which to grow out-of-line storage when it is exhausted, after the
77 // initial allocation.
78 static const unsigned outOfLineGrowthFactor
= 2;
80 struct PropertyMapEntry
{
81 UniquedStringImpl
* key
;
82 PropertyOffset offset
;
87 , offset(invalidOffset
)
92 PropertyMapEntry(UniquedStringImpl
* key
, PropertyOffset offset
, unsigned attributes
)
95 , attributes(attributes
)
100 class Structure final
: public JSCell
{
102 friend class StructureTransitionTable
;
105 static const unsigned StructureFlags
= Base::StructureFlags
| StructureIsImmortal
;
107 static Structure
* create(VM
&, JSGlobalObject
*, JSValue prototype
, const TypeInfo
&, const ClassInfo
*, IndexingType
= NonArray
, unsigned inlineCapacity
= 0);
112 void finishCreation(VM
& vm
)
114 Base::finishCreation(vm
);
116 ASSERT(m_prototype
.isObject() || m_prototype
.isNull());
119 void finishCreation(VM
& vm
, CreatingEarlyCellTag
)
121 Base::finishCreation(vm
, this, CreatingEarlyCell
);
123 ASSERT(m_prototype
.isNull());
124 ASSERT(!vm
.structureStructure
);
128 StructureID
id() const { return m_blob
.structureID(); }
129 int32_t objectInitializationBlob() const { return m_blob
.blobExcludingStructureID(); }
130 int64_t idBlob() const { return m_blob
.blob(); }
134 JSType type
= m_blob
.type();
135 return type
== ImpureProxyType
|| type
== PureForwardingProxyType
;
138 static void dumpStatistics();
140 JS_EXPORT_PRIVATE
static Structure
* addPropertyTransition(VM
&, Structure
*, PropertyName
, unsigned attributes
, PropertyOffset
&, PutPropertySlot::Context
= PutPropertySlot::UnknownContext
);
141 static Structure
* addPropertyTransitionToExistingStructureConcurrently(Structure
*, UniquedStringImpl
* uid
, unsigned attributes
, PropertyOffset
&);
142 JS_EXPORT_PRIVATE
static Structure
* addPropertyTransitionToExistingStructure(Structure
*, PropertyName
, unsigned attributes
, PropertyOffset
&);
143 static Structure
* removePropertyTransition(VM
&, Structure
*, PropertyName
, PropertyOffset
&);
144 JS_EXPORT_PRIVATE
static Structure
* changePrototypeTransition(VM
&, Structure
*, JSValue prototype
);
145 JS_EXPORT_PRIVATE
static Structure
* attributeChangeTransition(VM
&, Structure
*, PropertyName
, unsigned attributes
);
146 JS_EXPORT_PRIVATE
static Structure
* toCacheableDictionaryTransition(VM
&, Structure
*);
147 static Structure
* toUncacheableDictionaryTransition(VM
&, Structure
*);
148 JS_EXPORT_PRIVATE
static Structure
* sealTransition(VM
&, Structure
*);
149 JS_EXPORT_PRIVATE
static Structure
* freezeTransition(VM
&, Structure
*);
150 static Structure
* preventExtensionsTransition(VM
&, Structure
*);
151 JS_EXPORT_PRIVATE
static Structure
* nonPropertyTransition(VM
&, Structure
*, NonPropertyTransition
);
153 JS_EXPORT_PRIVATE
bool isSealed(VM
&);
154 JS_EXPORT_PRIVATE
bool isFrozen(VM
&);
155 bool isExtensible() const { return !preventExtensions(); }
156 bool putWillGrowOutOfLineStorage();
157 size_t suggestedNewOutOfLineStorageCapacity();
159 JS_EXPORT_PRIVATE Structure
* flattenDictionaryStructure(VM
&, JSObject
*);
161 static const bool needsDestruction
= true;
162 static void destroy(JSCell
*);
164 // These should be used with caution.
165 JS_EXPORT_PRIVATE PropertyOffset
addPropertyWithoutTransition(VM
&, PropertyName
, unsigned attributes
);
166 PropertyOffset
removePropertyWithoutTransition(VM
&, PropertyName
);
167 void setPrototypeWithoutTransition(VM
& vm
, JSValue prototype
) { m_prototype
.set(vm
, this, prototype
); }
169 bool isDictionary() const { return dictionaryKind() != NoneDictionaryKind
; }
170 bool isUncacheableDictionary() const { return dictionaryKind() == UncachedDictionaryKind
; }
172 bool propertyAccessesAreCacheable() { return dictionaryKind() != UncachedDictionaryKind
&& !typeInfo().prohibitsPropertyCaching(); }
174 // We use SlowPath in GetByIdStatus for structures that may get new impure properties later to prevent
175 // DFG from inlining property accesses since structures don't transition when a new impure property appears.
176 bool takesSlowPathInDFGForImpureProperty()
178 return typeInfo().hasImpureGetOwnPropertySlot();
182 TypeInfo
typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_blob
.typeInfo(m_outOfLineTypeFlags
); }
183 bool isObject() const { return typeInfo().isObject(); }
185 IndexingType
indexingType() const { return m_blob
.indexingType() & AllArrayTypes
; }
186 IndexingType
indexingTypeIncludingHistory() const { return m_blob
.indexingType(); }
188 bool mayInterceptIndexedAccesses() const
190 return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors
);
193 bool anyObjectInChainMayInterceptIndexedAccesses() const;
194 bool holesMustForwardToPrototype(VM
&) const;
196 bool needsSlowPutIndexing() const;
197 NonPropertyTransition
suggestedArrayStorageTransition() const;
199 JSGlobalObject
* globalObject() const { return m_globalObject
.get(); }
200 void setGlobalObject(VM
& vm
, JSGlobalObject
* globalObject
) { m_globalObject
.set(vm
, this, globalObject
); }
202 JSValue
storedPrototype() const { return m_prototype
.get(); }
203 JSObject
* storedPrototypeObject() const;
204 Structure
* storedPrototypeStructure() const;
205 JSValue
prototypeForLookup(ExecState
*) const;
206 JSValue
prototypeForLookup(JSGlobalObject
*) const;
207 JSValue
prototypeForLookup(CodeBlock
*) const;
208 StructureChain
* prototypeChain(VM
&, JSGlobalObject
*) const;
209 StructureChain
* prototypeChain(ExecState
*) const;
210 static void visitChildren(JSCell
*, SlotVisitor
&);
212 // Will just the prototype chain intercept this property access?
213 bool prototypeChainMayInterceptStoreTo(VM
&, PropertyName
);
215 Structure
* previousID() const
217 ASSERT(structure()->classInfo() == info());
219 return rareData()->previousID();
222 bool transitivelyTransitionedFrom(Structure
* structureToFind
);
224 unsigned outOfLineCapacity() const
226 ASSERT(checkOffsetConsistency());
228 unsigned outOfLineSize
= this->outOfLineSize();
233 if (outOfLineSize
<= initialOutOfLineCapacity
)
234 return initialOutOfLineCapacity
;
236 ASSERT(outOfLineSize
> initialOutOfLineCapacity
);
237 COMPILE_ASSERT(outOfLineGrowthFactor
== 2, outOfLineGrowthFactor_is_two
);
238 return WTF::roundUpToPowerOfTwo(outOfLineSize
);
240 unsigned outOfLineSize() const
242 ASSERT(checkOffsetConsistency());
243 ASSERT(structure()->classInfo() == info());
245 return numberOfOutOfLineSlotsForLastOffset(m_offset
);
247 bool hasInlineStorage() const
249 return !!m_inlineCapacity
;
251 unsigned inlineCapacity() const
253 return m_inlineCapacity
;
255 unsigned inlineSize() const
257 return std::min
<unsigned>(m_offset
+ 1, m_inlineCapacity
);
259 unsigned totalStorageSize() const
261 return numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
);
263 unsigned totalStorageCapacity() const
265 ASSERT(structure()->classInfo() == info());
266 return outOfLineCapacity() + inlineCapacity();
269 bool isValidOffset(PropertyOffset offset
) const
271 return JSC::isValidOffset(offset
)
272 && offset
<= m_offset
273 && (offset
< m_inlineCapacity
|| offset
>= firstOutOfLineOffset
);
276 bool couldHaveIndexingHeader() const
278 return hasIndexedProperties(indexingType())
279 || isTypedView(m_classInfo
->typedArrayStorageType
);
282 bool hasIndexingHeader(const JSCell
*) const;
284 bool masqueradesAsUndefined(JSGlobalObject
* lexicalGlobalObject
);
286 PropertyOffset
get(VM
&, PropertyName
);
287 PropertyOffset
get(VM
&, PropertyName
, unsigned& attributes
);
289 // This is a somewhat internalish method. It will call your functor while possibly holding the
290 // Structure's lock. There is no guarantee whether the lock is held or not in any particular
291 // call. So, you have to assume the worst. Also, the functor returns true if it wishes for you
292 // to continue or false if it's done.
293 template<typename Functor
>
294 void forEachPropertyConcurrently(const Functor
&);
296 PropertyOffset
getConcurrently(UniquedStringImpl
* uid
);
297 PropertyOffset
getConcurrently(UniquedStringImpl
* uid
, unsigned& attributes
);
299 Vector
<PropertyMapEntry
> getPropertiesConcurrently();
301 void setHasGetterSetterPropertiesWithProtoCheck(bool is__proto__
)
303 setHasGetterSetterProperties(true);
305 setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
308 void setContainsReadOnlyProperties() { setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); }
310 void setHasCustomGetterSetterPropertiesWithProtoCheck(bool is__proto__
)
312 setHasCustomGetterSetterProperties(true);
314 setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
319 ASSERT(checkOffsetConsistency());
320 return !JSC::isValidOffset(m_offset
);
323 void setCachedPropertyNameEnumerator(VM
&, JSPropertyNameEnumerator
*);
324 JSPropertyNameEnumerator
* cachedPropertyNameEnumerator() const;
325 bool canCachePropertyNameEnumerator() const;
326 bool canAccessPropertiesQuickly() const;
328 void getPropertyNamesFromStructure(VM
&, PropertyNameArray
&, EnumerationMode
);
330 JSString
* objectToStringValue()
334 return rareData()->objectToStringValue();
337 void setObjectToStringValue(VM
& vm
, JSString
* value
)
340 allocateRareData(vm
);
341 rareData()->setObjectToStringValue(vm
, value
);
344 const ClassInfo
* classInfo() const { return m_classInfo
; }
346 static ptrdiff_t structureIDOffset()
348 return OBJECT_OFFSETOF(Structure
, m_blob
) + StructureIDBlob::structureIDOffset();
351 static ptrdiff_t prototypeOffset()
353 return OBJECT_OFFSETOF(Structure
, m_prototype
);
356 static ptrdiff_t globalObjectOffset()
358 return OBJECT_OFFSETOF(Structure
, m_globalObject
);
361 static ptrdiff_t classInfoOffset()
363 return OBJECT_OFFSETOF(Structure
, m_classInfo
);
366 static ptrdiff_t indexingTypeOffset()
368 return OBJECT_OFFSETOF(Structure
, m_blob
) + StructureIDBlob::indexingTypeOffset();
371 static Structure
* createStructure(VM
&);
373 bool transitionWatchpointSetHasBeenInvalidated() const
375 return m_transitionWatchpointSet
.hasBeenInvalidated();
378 bool transitionWatchpointSetIsStillValid() const
380 return m_transitionWatchpointSet
.isStillValid();
383 bool dfgShouldWatchIfPossible() const
385 // FIXME: We would like to not watch things that are unprofitable to watch, like
386 // dictionaries. Unfortunately, we can't do such things: a dictionary could get flattened,
387 // in which case it will start to appear watchable and so the DFG will think that it is
388 // watching it. We should come up with a comprehensive story for not watching things that
389 // aren't profitable to watch.
390 // https://bugs.webkit.org/show_bug.cgi?id=133625
394 bool dfgShouldWatch() const
396 return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid();
399 void addTransitionWatchpoint(Watchpoint
* watchpoint
) const
401 ASSERT(transitionWatchpointSetIsStillValid());
402 m_transitionWatchpointSet
.add(watchpoint
);
405 void didTransitionFromThisStructure() const;
407 InlineWatchpointSet
& transitionWatchpointSet() const
409 return m_transitionWatchpointSet
;
412 WatchpointSet
* ensurePropertyReplacementWatchpointSet(VM
&, PropertyOffset
);
413 void startWatchingPropertyForReplacements(VM
& vm
, PropertyOffset offset
)
415 ensurePropertyReplacementWatchpointSet(vm
, offset
);
417 void startWatchingPropertyForReplacements(VM
&, PropertyName
);
418 WatchpointSet
* propertyReplacementWatchpointSet(PropertyOffset
);
419 void didReplaceProperty(PropertyOffset
);
420 void didCachePropertyReplacement(VM
&, PropertyOffset
);
422 void startWatchingInternalPropertiesIfNecessary(VM
& vm
)
424 if (LIKELY(didWatchInternalProperties()))
426 startWatchingInternalProperties(vm
);
429 void startWatchingInternalPropertiesIfNecessaryForEntireChain(VM
& vm
)
431 for (Structure
* structure
= this; structure
; structure
= structure
->storedPrototypeStructure())
432 structure
->startWatchingInternalPropertiesIfNecessary(vm
);
435 PassRefPtr
<StructureShape
> toStructureShape(JSValue
);
437 // Determines if the two structures match enough that this one could be used for allocations
439 bool canUseForAllocationsOf(Structure
*);
441 void dump(PrintStream
&) const;
442 void dumpInContext(PrintStream
&, DumpContext
*) const;
443 void dumpBrief(PrintStream
&, const CString
&) const;
445 static void dumpContextHeader(PrintStream
&);
451 NoneDictionaryKind
= 0,
452 CachedDictionaryKind
= 1,
453 UncachedDictionaryKind
= 2
457 #define DEFINE_BITFIELD(type, lowerName, upperName, width, offset) \
458 static const uint32_t s_##lowerName##Shift = offset;\
459 static const uint32_t s_##lowerName##Mask = ((1 << (width - 1)) | ((1 << (width - 1)) - 1));\
460 type lowerName() const { return static_cast<type>((m_bitField >> offset) & s_##lowerName##Mask); }\
461 void set##upperName(type newValue) \
463 m_bitField &= ~(s_##lowerName##Mask << offset);\
464 m_bitField |= (newValue & s_##lowerName##Mask) << offset;\
467 DEFINE_BITFIELD(DictionaryKind
, dictionaryKind
, DictionaryKind
, 2, 0);
468 DEFINE_BITFIELD(bool, isPinnedPropertyTable
, IsPinnedPropertyTable
, 1, 2);
469 DEFINE_BITFIELD(bool, hasGetterSetterProperties
, HasGetterSetterProperties
, 1, 3);
470 DEFINE_BITFIELD(bool, hasReadOnlyOrGetterSetterPropertiesExcludingProto
, HasReadOnlyOrGetterSetterPropertiesExcludingProto
, 1, 4);
471 DEFINE_BITFIELD(bool, hasNonEnumerableProperties
, HasNonEnumerableProperties
, 1, 5);
472 DEFINE_BITFIELD(unsigned, attributesInPrevious
, AttributesInPrevious
, 14, 6);
473 DEFINE_BITFIELD(bool, preventExtensions
, PreventExtensions
, 1, 20);
474 DEFINE_BITFIELD(bool, didTransition
, DidTransition
, 1, 21);
475 DEFINE_BITFIELD(bool, staticFunctionsReified
, StaticFunctionsReified
, 1, 22);
476 DEFINE_BITFIELD(bool, hasRareData
, HasRareData
, 1, 23);
477 DEFINE_BITFIELD(bool, hasBeenFlattenedBefore
, HasBeenFlattenedBefore
, 1, 24);
478 DEFINE_BITFIELD(bool, hasCustomGetterSetterProperties
, HasCustomGetterSetterProperties
, 1, 25);
479 DEFINE_BITFIELD(bool, didWatchInternalProperties
, DidWatchInternalProperties
, 1, 26);
482 friend class LLIntOffsetsExtractor
;
484 JS_EXPORT_PRIVATE
Structure(VM
&, JSGlobalObject
*, JSValue prototype
, const TypeInfo
&, const ClassInfo
*, IndexingType
, unsigned inlineCapacity
);
486 Structure(VM
&, Structure
*);
488 static Structure
* create(VM
&, Structure
*);
490 static Structure
* addPropertyTransitionToExistingStructureImpl(Structure
*, UniquedStringImpl
* uid
, unsigned attributes
, PropertyOffset
&);
492 // This will return the structure that has a usable property table, that property table,
493 // and the list of structures that we visited before we got to it. If it returns a
494 // non-null structure, it will also lock the structure that it returns; it is your job
496 void findStructuresAndMapForMaterialization(Vector
<Structure
*, 8>& structures
, Structure
*&, PropertyTable
*&);
498 static Structure
* toDictionaryTransition(VM
&, Structure
*, DictionaryKind
);
500 PropertyOffset
add(VM
&, PropertyName
, unsigned attributes
);
501 PropertyOffset
remove(PropertyName
);
503 void createPropertyMap(const GCSafeConcurrentJITLocker
&, VM
&, unsigned keyCount
= 0);
504 void checkConsistency();
506 WriteBarrier
<PropertyTable
>& propertyTable();
507 PropertyTable
* takePropertyTableOrCloneIfPinned(VM
&);
508 PropertyTable
* copyPropertyTable(VM
&);
509 PropertyTable
* copyPropertyTableForPinning(VM
&);
510 JS_EXPORT_PRIVATE
void materializePropertyMap(VM
&);
511 ALWAYS_INLINE
void materializePropertyMapIfNecessary(VM
& vm
, DeferGC
&)
513 ASSERT(!isCompilationThread());
514 ASSERT(structure()->classInfo() == info());
515 ASSERT(checkOffsetConsistency());
516 if (!propertyTable() && previousID())
517 materializePropertyMap(vm
);
519 ALWAYS_INLINE
void materializePropertyMapIfNecessary(VM
& vm
, PropertyTable
*& table
)
521 ASSERT(!isCompilationThread());
522 ASSERT(structure()->classInfo() == info());
523 ASSERT(checkOffsetConsistency());
524 table
= propertyTable().get();
525 if (!table
&& previousID()) {
526 DeferGC
deferGC(vm
.heap
);
527 materializePropertyMap(vm
);
528 table
= propertyTable().get();
531 void materializePropertyMapIfNecessaryForPinning(VM
& vm
, DeferGC
&)
533 ASSERT(structure()->classInfo() == info());
534 checkOffsetConsistency();
535 if (!propertyTable())
536 materializePropertyMap(vm
);
539 void setPreviousID(VM
& vm
, Structure
* structure
)
542 rareData()->setPreviousID(vm
, structure
);
544 m_previousOrRareData
.set(vm
, this, structure
);
547 void clearPreviousID()
550 rareData()->clearPreviousID();
552 m_previousOrRareData
.clear();
555 int transitionCount() const
557 // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
558 return numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
);
561 bool isValid(JSGlobalObject
*, StructureChain
* cachedPrototypeChain
) const;
562 bool isValid(ExecState
*, StructureChain
* cachedPrototypeChain
) const;
566 Structure
* previous() const
568 ASSERT(!hasRareData());
569 return static_cast<Structure
*>(m_previousOrRareData
.get());
572 StructureRareData
* rareData() const
574 ASSERT(hasRareData());
575 return static_cast<StructureRareData
*>(m_previousOrRareData
.get());
578 bool checkOffsetConsistency() const;
580 JS_EXPORT_PRIVATE
void allocateRareData(VM
&);
582 void startWatchingInternalProperties(VM
&);
584 static const int s_maxTransitionLength
= 64;
585 static const int s_maxTransitionLengthForNonEvalPutById
= 512;
587 // These need to be properly aligned at the beginning of the 'Structure'
588 // part of the object.
589 StructureIDBlob m_blob
;
590 TypeInfo::OutOfLineTypeFlags m_outOfLineTypeFlags
;
592 WriteBarrier
<JSGlobalObject
> m_globalObject
;
593 WriteBarrier
<Unknown
> m_prototype
;
594 mutable WriteBarrier
<StructureChain
> m_cachedPrototypeChain
;
596 WriteBarrier
<JSCell
> m_previousOrRareData
;
598 RefPtr
<UniquedStringImpl
> m_nameInPrevious
;
600 const ClassInfo
* m_classInfo
;
602 StructureTransitionTable m_transitionTable
;
604 // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread.
605 WriteBarrier
<PropertyTable
> m_propertyTableUnsafe
;
607 mutable InlineWatchpointSet m_transitionWatchpointSet
;
609 COMPILE_ASSERT(firstOutOfLineOffset
< 256, firstOutOfLineOffset_fits
);
611 // m_offset does not account for anonymous slots
612 PropertyOffset m_offset
;
614 uint8_t m_inlineCapacity
;
616 ConcurrentJITLock m_lock
;
623 #endif // Structure_h