2 * Copyright (C) 2008, 2009, 2012, 2013 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/StringImpl.h>
57 class LLIntOffsetsExtractor
;
58 class PropertyNameArray
;
59 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 class Structure
: public JSCell
{
77 friend class StructureTransitionTable
;
81 static Structure
* create(VM
&, JSGlobalObject
*, JSValue prototype
, const TypeInfo
&, const ClassInfo
*, IndexingType
= NonArray
, unsigned inlineCapacity
= 0);
86 void finishCreation(VM
& vm
)
88 Base::finishCreation(vm
);
90 ASSERT(m_prototype
.isObject() || m_prototype
.isNull());
93 void finishCreation(VM
& vm
, CreatingEarlyCellTag
)
95 Base::finishCreation(vm
, this, CreatingEarlyCell
);
97 ASSERT(m_prototype
.isNull());
98 ASSERT(!vm
.structureStructure
);
102 StructureID
id() const { return m_blob
.structureID(); }
103 int32_t objectInitializationBlob() const { return m_blob
.blobExcludingStructureID(); }
104 int64_t idBlob() const { return m_blob
.blob(); }
108 JSType type
= m_blob
.type();
109 return type
== ImpureProxyType
|| type
== PureForwardingProxyType
;
112 static void dumpStatistics();
114 JS_EXPORT_PRIVATE
static Structure
* addPropertyTransition(VM
&, Structure
*, PropertyName
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
&, PutPropertySlot::Context
= PutPropertySlot::UnknownContext
);
115 static Structure
* addPropertyTransitionToExistingStructureConcurrently(Structure
*, StringImpl
* uid
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
&);
116 JS_EXPORT_PRIVATE
static Structure
* addPropertyTransitionToExistingStructure(Structure
*, PropertyName
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
&);
117 static Structure
* removePropertyTransition(VM
&, Structure
*, PropertyName
, PropertyOffset
&);
118 JS_EXPORT_PRIVATE
static Structure
* changePrototypeTransition(VM
&, Structure
*, JSValue prototype
);
119 JS_EXPORT_PRIVATE
static Structure
* despecifyFunctionTransition(VM
&, Structure
*, PropertyName
);
120 static Structure
* attributeChangeTransition(VM
&, Structure
*, PropertyName
, unsigned attributes
);
121 JS_EXPORT_PRIVATE
static Structure
* toCacheableDictionaryTransition(VM
&, Structure
*);
122 static Structure
* toUncacheableDictionaryTransition(VM
&, Structure
*);
123 static Structure
* sealTransition(VM
&, Structure
*);
124 static Structure
* freezeTransition(VM
&, Structure
*);
125 static Structure
* preventExtensionsTransition(VM
&, Structure
*);
126 static Structure
* nonPropertyTransition(VM
&, Structure
*, NonPropertyTransition
);
130 bool isExtensible() const { return !m_preventExtensions
; }
131 bool didTransition() const { return m_didTransition
; }
132 bool putWillGrowOutOfLineStorage();
133 size_t suggestedNewOutOfLineStorageCapacity();
135 JS_EXPORT_PRIVATE Structure
* flattenDictionaryStructure(VM
&, JSObject
*);
137 static const bool needsDestruction
= true;
138 static const bool hasImmortalStructure
= true;
139 static void destroy(JSCell
*);
141 // These should be used with caution.
142 JS_EXPORT_PRIVATE PropertyOffset
addPropertyWithoutTransition(VM
&, PropertyName
, unsigned attributes
, JSCell
* specificValue
);
143 PropertyOffset
removePropertyWithoutTransition(VM
&, PropertyName
);
144 void setPrototypeWithoutTransition(VM
& vm
, JSValue prototype
) { m_prototype
.set(vm
, this, prototype
); }
146 bool isDictionary() const { return m_dictionaryKind
!= NoneDictionaryKind
; }
147 bool isUncacheableDictionary() const { return m_dictionaryKind
== UncachedDictionaryKind
; }
149 bool hasBeenFlattenedBefore() const { return m_hasBeenFlattenedBefore
; }
151 bool propertyAccessesAreCacheable() { return m_dictionaryKind
!= UncachedDictionaryKind
&& !typeInfo().prohibitsPropertyCaching(); }
153 // We use SlowPath in GetByIdStatus for structures that may get new impure properties later to prevent
154 // DFG from inlining property accesses since structures don't transition when a new impure property appears.
155 bool takesSlowPathInDFGForImpureProperty()
157 return typeInfo().hasImpureGetOwnPropertySlot();
161 TypeInfo
typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_blob
.typeInfo(m_outOfLineTypeFlags
); }
162 bool isObject() const { return typeInfo().isObject(); }
164 IndexingType
indexingType() const { return m_blob
.indexingType() & AllArrayTypes
; }
165 IndexingType
indexingTypeIncludingHistory() const { return m_blob
.indexingType(); }
167 bool mayInterceptIndexedAccesses() const
169 return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors
);
172 bool anyObjectInChainMayInterceptIndexedAccesses() const;
173 bool holesMustForwardToPrototype(VM
&) const;
175 bool needsSlowPutIndexing() const;
176 NonPropertyTransition
suggestedArrayStorageTransition() const;
178 JSGlobalObject
* globalObject() const { return m_globalObject
.get(); }
179 void setGlobalObject(VM
& vm
, JSGlobalObject
* globalObject
) { m_globalObject
.set(vm
, this, globalObject
); }
181 JSValue
storedPrototype() const { return m_prototype
.get(); }
182 JSObject
* storedPrototypeObject() const;
183 Structure
* storedPrototypeStructure() const;
184 JSValue
prototypeForLookup(ExecState
*) const;
185 JSValue
prototypeForLookup(JSGlobalObject
*) const;
186 JSValue
prototypeForLookup(CodeBlock
*) const;
187 StructureChain
* prototypeChain(VM
&, JSGlobalObject
*) const;
188 StructureChain
* prototypeChain(ExecState
*) const;
189 static void visitChildren(JSCell
*, SlotVisitor
&);
191 // Will just the prototype chain intercept this property access?
192 bool prototypeChainMayInterceptStoreTo(VM
&, PropertyName
);
194 bool transitionDidInvolveSpecificValue() const { return !!m_specificValueInPrevious
; }
196 Structure
* previousID() const
198 ASSERT(structure()->classInfo() == info());
200 return rareData()->previousID();
203 bool transitivelyTransitionedFrom(Structure
* structureToFind
);
205 unsigned outOfLineCapacity() const
207 ASSERT(checkOffsetConsistency());
209 unsigned outOfLineSize
= this->outOfLineSize();
214 if (outOfLineSize
<= initialOutOfLineCapacity
)
215 return initialOutOfLineCapacity
;
217 ASSERT(outOfLineSize
> initialOutOfLineCapacity
);
218 COMPILE_ASSERT(outOfLineGrowthFactor
== 2, outOfLineGrowthFactor_is_two
);
219 return WTF::roundUpToPowerOfTwo(outOfLineSize
);
221 unsigned outOfLineSize() const
223 ASSERT(checkOffsetConsistency());
224 ASSERT(structure()->classInfo() == info());
226 return numberOfOutOfLineSlotsForLastOffset(m_offset
);
228 bool hasInlineStorage() const
230 return !!m_inlineCapacity
;
232 unsigned inlineCapacity() const
234 return m_inlineCapacity
;
236 unsigned inlineSize() const
238 return std::min
<unsigned>(m_offset
+ 1, m_inlineCapacity
);
240 unsigned totalStorageSize() const
242 return numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
);
244 unsigned totalStorageCapacity() const
246 ASSERT(structure()->classInfo() == info());
247 return outOfLineCapacity() + inlineCapacity();
250 bool isValidOffset(PropertyOffset offset
) const
252 return JSC::isValidOffset(offset
)
253 && offset
<= m_offset
254 && (offset
< m_inlineCapacity
|| offset
>= firstOutOfLineOffset
);
257 bool couldHaveIndexingHeader() const
259 return hasIndexedProperties(indexingType())
260 || isTypedView(m_classInfo
->typedArrayStorageType
);
263 bool hasIndexingHeader(const JSCell
*) const;
265 bool masqueradesAsUndefined(JSGlobalObject
* lexicalGlobalObject
);
267 PropertyOffset
get(VM
&, PropertyName
);
268 PropertyOffset
get(VM
&, const WTF::String
& name
);
269 PropertyOffset
get(VM
&, PropertyName
, unsigned& attributes
, JSCell
*& specificValue
);
271 PropertyOffset
getConcurrently(VM
&, StringImpl
* uid
);
272 PropertyOffset
getConcurrently(VM
&, StringImpl
* uid
, unsigned& attributes
, JSCell
*& specificValue
);
274 bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties
; }
275 bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
; }
276 void setHasGetterSetterProperties(bool is__proto__
)
278 m_hasGetterSetterProperties
= true;
280 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
= true;
283 bool hasCustomGetterSetterProperties() const { return m_hasCustomGetterSetterProperties
; }
284 void setHasCustomGetterSetterProperties(bool is__proto__
)
286 m_hasCustomGetterSetterProperties
= true;
288 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
= true;
291 void setContainsReadOnlyProperties()
293 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
= true;
296 bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties
; }
300 ASSERT(checkOffsetConsistency());
301 return !JSC::isValidOffset(m_offset
);
304 JS_EXPORT_PRIVATE
void despecifyDictionaryFunction(VM
&, PropertyName
);
305 void disableSpecificFunctionTracking() { m_specificFunctionThrashCount
= maxSpecificFunctionThrashCount
; }
307 void setEnumerationCache(VM
&, JSPropertyNameIterator
* enumerationCache
); // Defined in JSPropertyNameIterator.h.
308 JSPropertyNameIterator
* enumerationCache(); // Defined in JSPropertyNameIterator.h.
309 void getPropertyNamesFromStructure(VM
&, PropertyNameArray
&, EnumerationMode
);
311 JSString
* objectToStringValue()
315 return rareData()->objectToStringValue();
318 void setObjectToStringValue(VM
& vm
, JSString
* value
)
321 allocateRareData(vm
);
322 rareData()->setObjectToStringValue(vm
, value
);
325 bool staticFunctionsReified()
327 return m_staticFunctionReified
;
330 void setStaticFunctionsReified()
332 m_staticFunctionReified
= true;
335 const ClassInfo
* classInfo() const { return m_classInfo
; }
337 static ptrdiff_t structureIDOffset()
339 return OBJECT_OFFSETOF(Structure
, m_blob
) + StructureIDBlob::structureIDOffset();
342 static ptrdiff_t prototypeOffset()
344 return OBJECT_OFFSETOF(Structure
, m_prototype
);
347 static ptrdiff_t globalObjectOffset()
349 return OBJECT_OFFSETOF(Structure
, m_globalObject
);
352 static ptrdiff_t classInfoOffset()
354 return OBJECT_OFFSETOF(Structure
, m_classInfo
);
357 static ptrdiff_t indexingTypeOffset()
359 return OBJECT_OFFSETOF(Structure
, m_blob
) + StructureIDBlob::indexingTypeOffset();
362 static Structure
* createStructure(VM
&);
364 bool transitionWatchpointSetHasBeenInvalidated() const
366 return m_transitionWatchpointSet
.hasBeenInvalidated();
369 bool transitionWatchpointSetIsStillValid() const
371 return m_transitionWatchpointSet
.isStillValid();
374 void addTransitionWatchpoint(Watchpoint
* watchpoint
) const
376 ASSERT(transitionWatchpointSetIsStillValid());
377 m_transitionWatchpointSet
.add(watchpoint
);
380 void notifyTransitionFromThisStructure() const
382 m_transitionWatchpointSet
.fireAll();
385 InlineWatchpointSet
& transitionWatchpointSet() const
387 return m_transitionWatchpointSet
;
390 void dump(PrintStream
&) const;
391 void dumpInContext(PrintStream
&, DumpContext
*) const;
392 void dumpBrief(PrintStream
&, const CString
&) const;
394 static void dumpContextHeader(PrintStream
&);
399 friend class LLIntOffsetsExtractor
;
401 JS_EXPORT_PRIVATE
Structure(VM
&, JSGlobalObject
*, JSValue prototype
, const TypeInfo
&, const ClassInfo
*, IndexingType
, unsigned inlineCapacity
);
403 Structure(VM
&, Structure
*);
405 static Structure
* create(VM
&, Structure
*);
407 static Structure
* addPropertyTransitionToExistingStructureImpl(Structure
*, StringImpl
* uid
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
&);
409 // This will return the structure that has a usable property table, that property table,
410 // and the list of structures that we visited before we got to it. If it returns a
411 // non-null structure, it will also lock the structure that it returns; it is your job
413 void findStructuresAndMapForMaterialization(Vector
<Structure
*, 8>& structures
, Structure
*&, PropertyTable
*&);
416 NoneDictionaryKind
= 0,
417 CachedDictionaryKind
= 1,
418 UncachedDictionaryKind
= 2
420 static Structure
* toDictionaryTransition(VM
&, Structure
*, DictionaryKind
);
422 PropertyOffset
putSpecificValue(VM
&, PropertyName
, unsigned attributes
, JSCell
* specificValue
);
423 PropertyOffset
remove(PropertyName
);
425 void createPropertyMap(const GCSafeConcurrentJITLocker
&, VM
&, unsigned keyCount
= 0);
426 void checkConsistency();
428 bool despecifyFunction(VM
&, PropertyName
);
429 void despecifyAllFunctions(VM
&);
431 WriteBarrier
<PropertyTable
>& propertyTable();
432 PropertyTable
* takePropertyTableOrCloneIfPinned(VM
&);
433 PropertyTable
* copyPropertyTable(VM
&);
434 PropertyTable
* copyPropertyTableForPinning(VM
&);
435 JS_EXPORT_PRIVATE
void materializePropertyMap(VM
&);
436 ALWAYS_INLINE
void materializePropertyMapIfNecessary(VM
& vm
, DeferGC
&)
438 ASSERT(!isCompilationThread());
439 ASSERT(structure()->classInfo() == info());
440 ASSERT(checkOffsetConsistency());
441 if (!propertyTable() && previousID())
442 materializePropertyMap(vm
);
444 ALWAYS_INLINE
void materializePropertyMapIfNecessary(VM
& vm
, PropertyTable
*& table
)
446 ASSERT(!isCompilationThread());
447 ASSERT(structure()->classInfo() == info());
448 ASSERT(checkOffsetConsistency());
449 table
= propertyTable().get();
450 if (!table
&& previousID()) {
451 DeferGC
deferGC(vm
.heap
);
452 materializePropertyMap(vm
);
453 table
= propertyTable().get();
456 void materializePropertyMapIfNecessaryForPinning(VM
& vm
, DeferGC
&)
458 ASSERT(structure()->classInfo() == info());
459 checkOffsetConsistency();
460 if (!propertyTable())
461 materializePropertyMap(vm
);
464 void setPreviousID(VM
& vm
, Structure
* structure
)
467 rareData()->setPreviousID(vm
, structure
);
469 m_previousOrRareData
.set(vm
, this, structure
);
472 void clearPreviousID()
475 rareData()->clearPreviousID();
477 m_previousOrRareData
.clear();
480 int transitionCount() const
482 // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
483 return numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
);
486 bool isValid(JSGlobalObject
*, StructureChain
* cachedPrototypeChain
) const;
487 bool isValid(ExecState
*, StructureChain
* cachedPrototypeChain
) const;
491 Structure
* previous() const
493 ASSERT(!m_hasRareData
);
494 return static_cast<Structure
*>(m_previousOrRareData
.get());
497 StructureRareData
* rareData() const
499 ASSERT(m_hasRareData
);
500 return static_cast<StructureRareData
*>(m_previousOrRareData
.get());
503 bool checkOffsetConsistency() const;
505 void allocateRareData(VM
&);
506 void cloneRareDataFrom(VM
&, const Structure
*);
508 static const int s_maxTransitionLength
= 64;
509 static const int s_maxTransitionLengthForNonEvalPutById
= 512;
511 static const unsigned maxSpecificFunctionThrashCount
= 3;
513 // These need to be properly aligned at the beginning of the 'Structure'
514 // part of the object.
515 StructureIDBlob m_blob
;
516 TypeInfo::OutOfLineTypeFlags m_outOfLineTypeFlags
;
518 WriteBarrier
<JSGlobalObject
> m_globalObject
;
519 WriteBarrier
<Unknown
> m_prototype
;
520 mutable WriteBarrier
<StructureChain
> m_cachedPrototypeChain
;
522 WriteBarrier
<JSCell
> m_previousOrRareData
;
524 RefPtr
<StringImpl
> m_nameInPrevious
;
525 WriteBarrier
<JSCell
> m_specificValueInPrevious
;
527 const ClassInfo
* m_classInfo
;
529 StructureTransitionTable m_transitionTable
;
531 // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread.
532 WriteBarrier
<PropertyTable
> m_propertyTableUnsafe
;
534 mutable InlineWatchpointSet m_transitionWatchpointSet
;
536 COMPILE_ASSERT(firstOutOfLineOffset
< 256, firstOutOfLineOffset_fits
);
538 // m_offset does not account for anonymous slots
539 PropertyOffset m_offset
;
541 uint8_t m_inlineCapacity
;
543 ConcurrentJITLock m_lock
;
545 unsigned m_dictionaryKind
: 2;
546 bool m_hasBeenFlattenedBefore
: 1;
547 bool m_isPinnedPropertyTable
: 1;
548 bool m_hasGetterSetterProperties
: 1;
549 bool m_hasCustomGetterSetterProperties
: 1;
550 bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
: 1;
551 bool m_hasNonEnumerableProperties
: 1;
552 unsigned m_attributesInPrevious
: 14;
553 unsigned m_specificFunctionThrashCount
: 2;
554 unsigned m_preventExtensions
: 1;
555 unsigned m_didTransition
: 1;
556 unsigned m_staticFunctionReified
: 1;
557 bool m_hasRareData
: 1;
562 #endif // Structure_h