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 COMPUTER, 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 COMPUTER, 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 "IndexingType.h"
31 #include "JSCJSValue.h"
34 #include "PropertyName.h"
35 #include "PropertyNameArray.h"
36 #include "PropertyOffset.h"
38 #include "StructureRareData.h"
39 #include "StructureTransitionTable.h"
40 #include "JSTypeInfo.h"
41 #include "Watchpoint.h"
42 #include <wtf/PassRefPtr.h>
43 #include <wtf/RefCounted.h>
44 #include <wtf/text/StringImpl.h>
49 class LLIntOffsetsExtractor
;
50 class PropertyNameArray
;
51 class PropertyNameArrayData
;
57 // The out-of-line property storage capacity to use when first allocating out-of-line
58 // storage. Note that all objects start out without having any out-of-line storage;
59 // this comes into play only on the first property store that exhausts inline storage.
60 static const unsigned initialOutOfLineCapacity
= 4;
62 // The factor by which to grow out-of-line storage when it is exhausted, after the
63 // initial allocation.
64 static const unsigned outOfLineGrowthFactor
= 2;
66 class Structure
: public JSCell
{
68 friend class StructureTransitionTable
;
72 static Structure
* create(VM
&, JSGlobalObject
*, JSValue prototype
, const TypeInfo
&, const ClassInfo
*, IndexingType
= NonArray
, unsigned inlineCapacity
= 0);
75 void finishCreation(VM
& vm
)
77 Base::finishCreation(vm
);
79 ASSERT(m_prototype
.isObject() || m_prototype
.isNull());
82 void finishCreation(VM
& vm
, CreatingEarlyCellTag
)
84 Base::finishCreation(vm
, this, CreatingEarlyCell
);
86 ASSERT(m_prototype
.isNull());
87 ASSERT(!vm
.structureStructure
);
91 static void dumpStatistics();
93 JS_EXPORT_PRIVATE
static Structure
* addPropertyTransition(VM
&, Structure
*, PropertyName
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
&);
94 JS_EXPORT_PRIVATE
static Structure
* addPropertyTransitionToExistingStructure(Structure
*, PropertyName
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
&);
95 static Structure
* removePropertyTransition(VM
&, Structure
*, PropertyName
, PropertyOffset
&);
96 JS_EXPORT_PRIVATE
static Structure
* changePrototypeTransition(VM
&, Structure
*, JSValue prototype
);
97 JS_EXPORT_PRIVATE
static Structure
* despecifyFunctionTransition(VM
&, Structure
*, PropertyName
);
98 static Structure
* attributeChangeTransition(VM
&, Structure
*, PropertyName
, unsigned attributes
);
99 static Structure
* toCacheableDictionaryTransition(VM
&, Structure
*);
100 static Structure
* toUncacheableDictionaryTransition(VM
&, Structure
*);
101 static Structure
* sealTransition(VM
&, Structure
*);
102 static Structure
* freezeTransition(VM
&, Structure
*);
103 static Structure
* preventExtensionsTransition(VM
&, Structure
*);
104 static Structure
* nonPropertyTransition(VM
&, Structure
*, NonPropertyTransition
);
108 bool isExtensible() const { return !m_preventExtensions
; }
109 bool didTransition() const { return m_didTransition
; }
110 bool putWillGrowOutOfLineStorage();
111 JS_EXPORT_PRIVATE
size_t suggestedNewOutOfLineStorageCapacity();
113 Structure
* flattenDictionaryStructure(VM
&, JSObject
*);
115 static const bool needsDestruction
= true;
116 static const bool hasImmortalStructure
= true;
117 static void destroy(JSCell
*);
119 // These should be used with caution.
120 JS_EXPORT_PRIVATE PropertyOffset
addPropertyWithoutTransition(VM
&, PropertyName
, unsigned attributes
, JSCell
* specificValue
);
121 PropertyOffset
removePropertyWithoutTransition(VM
&, PropertyName
);
122 void setPrototypeWithoutTransition(VM
& vm
, JSValue prototype
) { m_prototype
.set(vm
, this, prototype
); }
124 bool isDictionary() const { return m_dictionaryKind
!= NoneDictionaryKind
; }
125 bool isUncacheableDictionary() const { return m_dictionaryKind
== UncachedDictionaryKind
; }
127 bool propertyAccessesAreCacheable() { return m_dictionaryKind
!= UncachedDictionaryKind
&& !typeInfo().prohibitsPropertyCaching(); }
130 const TypeInfo
& typeInfo() const { ASSERT(structure()->classInfo() == &s_info
); return m_typeInfo
; }
131 bool isObject() const { return typeInfo().isObject(); }
133 IndexingType
indexingType() const { return m_indexingType
& AllArrayTypes
; }
134 IndexingType
indexingTypeIncludingHistory() const { return m_indexingType
; }
136 bool mayInterceptIndexedAccesses() const
138 return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors
);
141 bool anyObjectInChainMayInterceptIndexedAccesses() const;
143 bool needsSlowPutIndexing() const;
144 NonPropertyTransition
suggestedArrayStorageTransition() const;
146 JSGlobalObject
* globalObject() const { return m_globalObject
.get(); }
147 void setGlobalObject(VM
& vm
, JSGlobalObject
* globalObject
) { m_globalObject
.set(vm
, this, globalObject
); }
149 JSValue
storedPrototype() const { return m_prototype
.get(); }
150 JSValue
prototypeForLookup(ExecState
*) const;
151 JSValue
prototypeForLookup(JSGlobalObject
*) const;
152 JSValue
prototypeForLookup(CodeBlock
*) const;
153 StructureChain
* prototypeChain(VM
&, JSGlobalObject
*) const;
154 StructureChain
* prototypeChain(ExecState
*) const;
155 static void visitChildren(JSCell
*, SlotVisitor
&);
157 // Will just the prototype chain intercept this property access?
158 bool prototypeChainMayInterceptStoreTo(VM
&, PropertyName
);
160 bool transitionDidInvolveSpecificValue() const { return !!m_specificValueInPrevious
; }
162 Structure
* previousID() const
164 ASSERT(structure()->classInfo() == &s_info
);
165 if (typeInfo().structureHasRareData())
166 return rareData()->previousID();
169 bool transitivelyTransitionedFrom(Structure
* structureToFind
);
171 unsigned outOfLineCapacity() const
173 ASSERT(checkOffsetConsistency());
175 unsigned outOfLineSize
= this->outOfLineSize();
180 if (outOfLineSize
<= initialOutOfLineCapacity
)
181 return initialOutOfLineCapacity
;
183 ASSERT(outOfLineSize
> initialOutOfLineCapacity
);
184 COMPILE_ASSERT(outOfLineGrowthFactor
== 2, outOfLineGrowthFactor_is_two
);
185 return WTF::roundUpToPowerOfTwo(outOfLineSize
);
187 unsigned outOfLineSize() const
189 ASSERT(checkOffsetConsistency());
190 ASSERT(structure()->classInfo() == &s_info
);
192 return numberOfOutOfLineSlotsForLastOffset(m_offset
);
194 bool hasInlineStorage() const
196 return !!m_inlineCapacity
;
198 unsigned inlineCapacity() const
200 return m_inlineCapacity
;
202 unsigned inlineSize() const
204 return std::min
<unsigned>(m_offset
+ 1, m_inlineCapacity
);
206 unsigned totalStorageSize() const
208 return numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
);
210 unsigned totalStorageCapacity() const
212 ASSERT(structure()->classInfo() == &s_info
);
213 return outOfLineCapacity() + inlineCapacity();
216 PropertyOffset
firstValidOffset() const
218 if (hasInlineStorage())
220 return firstOutOfLineOffset
;
222 PropertyOffset
lastValidOffset() const
226 bool isValidOffset(PropertyOffset offset
) const
228 return offset
>= firstValidOffset()
229 && offset
<= lastValidOffset();
232 bool masqueradesAsUndefined(JSGlobalObject
* lexicalGlobalObject
);
234 PropertyOffset
get(VM
&, PropertyName
);
235 PropertyOffset
get(VM
&, const WTF::String
& name
);
236 JS_EXPORT_PRIVATE PropertyOffset
get(VM
&, PropertyName
, unsigned& attributes
, JSCell
*& specificValue
);
238 bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties
; }
239 bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
; }
240 void setHasGetterSetterProperties(bool is__proto__
)
242 m_hasGetterSetterProperties
= true;
244 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
= true;
246 void setContainsReadOnlyProperties()
248 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
= true;
251 bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties
; }
255 ASSERT(checkOffsetConsistency());
256 return !JSC::isValidOffset(m_offset
);
259 JS_EXPORT_PRIVATE
void despecifyDictionaryFunction(VM
&, PropertyName
);
260 void disableSpecificFunctionTracking() { m_specificFunctionThrashCount
= maxSpecificFunctionThrashCount
; }
262 void setEnumerationCache(VM
&, JSPropertyNameIterator
* enumerationCache
); // Defined in JSPropertyNameIterator.h.
263 JSPropertyNameIterator
* enumerationCache(); // Defined in JSPropertyNameIterator.h.
264 void getPropertyNamesFromStructure(VM
&, PropertyNameArray
&, EnumerationMode
);
266 JSString
* objectToStringValue()
268 if (!typeInfo().structureHasRareData())
270 return rareData()->objectToStringValue();
273 void setObjectToStringValue(VM
& vm
, const JSCell
* owner
, JSString
* value
)
275 if (!typeInfo().structureHasRareData())
276 allocateRareData(vm
);
277 rareData()->setObjectToStringValue(vm
, owner
, value
);
280 bool staticFunctionsReified()
282 return m_staticFunctionReified
;
285 void setStaticFunctionsReified()
287 m_staticFunctionReified
= true;
290 const ClassInfo
* classInfo() const { return m_classInfo
; }
292 static ptrdiff_t prototypeOffset()
294 return OBJECT_OFFSETOF(Structure
, m_prototype
);
297 static ptrdiff_t globalObjectOffset()
299 return OBJECT_OFFSETOF(Structure
, m_globalObject
);
302 static ptrdiff_t typeInfoFlagsOffset()
304 return OBJECT_OFFSETOF(Structure
, m_typeInfo
) + TypeInfo::flagsOffset();
307 static ptrdiff_t typeInfoTypeOffset()
309 return OBJECT_OFFSETOF(Structure
, m_typeInfo
) + TypeInfo::typeOffset();
312 static ptrdiff_t classInfoOffset()
314 return OBJECT_OFFSETOF(Structure
, m_classInfo
);
317 static ptrdiff_t indexingTypeOffset()
319 return OBJECT_OFFSETOF(Structure
, m_indexingType
);
322 static Structure
* createStructure(VM
&);
324 bool transitionWatchpointSetHasBeenInvalidated() const
326 return m_transitionWatchpointSet
.hasBeenInvalidated();
329 bool transitionWatchpointSetIsStillValid() const
331 return m_transitionWatchpointSet
.isStillValid();
334 void addTransitionWatchpoint(Watchpoint
* watchpoint
) const
336 ASSERT(transitionWatchpointSetIsStillValid());
337 m_transitionWatchpointSet
.add(watchpoint
);
340 void notifyTransitionFromThisStructure() const
342 m_transitionWatchpointSet
.notifyWrite();
345 static JS_EXPORTDATA
const ClassInfo s_info
;
348 friend class LLIntOffsetsExtractor
;
350 JS_EXPORT_PRIVATE
Structure(VM
&, JSGlobalObject
*, JSValue prototype
, const TypeInfo
&, const ClassInfo
*, IndexingType
, unsigned inlineCapacity
);
352 Structure(VM
&, const Structure
*);
354 static Structure
* create(VM
&, const Structure
*);
357 NoneDictionaryKind
= 0,
358 CachedDictionaryKind
= 1,
359 UncachedDictionaryKind
= 2
361 static Structure
* toDictionaryTransition(VM
&, Structure
*, DictionaryKind
);
363 PropertyOffset
putSpecificValue(VM
&, PropertyName
, unsigned attributes
, JSCell
* specificValue
);
364 PropertyOffset
remove(PropertyName
);
366 void createPropertyMap(VM
&, unsigned keyCount
= 0);
367 void checkConsistency();
369 bool despecifyFunction(VM
&, PropertyName
);
370 void despecifyAllFunctions(VM
&);
372 WriteBarrier
<PropertyTable
>& propertyTable();
373 PropertyTable
* takePropertyTableOrCloneIfPinned(VM
&, Structure
* owner
);
374 PropertyTable
* copyPropertyTable(VM
&, Structure
* owner
);
375 PropertyTable
* copyPropertyTableForPinning(VM
&, Structure
* owner
);
376 JS_EXPORT_PRIVATE
void materializePropertyMap(VM
&);
377 void materializePropertyMapIfNecessary(VM
& vm
)
379 ASSERT(structure()->classInfo() == &s_info
);
380 ASSERT(checkOffsetConsistency());
381 if (!propertyTable() && previousID())
382 materializePropertyMap(vm
);
384 void materializePropertyMapIfNecessaryForPinning(VM
& vm
)
386 ASSERT(structure()->classInfo() == &s_info
);
387 checkOffsetConsistency();
388 if (!propertyTable())
389 materializePropertyMap(vm
);
392 void setPreviousID(VM
& vm
, Structure
* transition
, Structure
* structure
)
394 if (typeInfo().structureHasRareData())
395 rareData()->setPreviousID(vm
, transition
, structure
);
397 m_previousOrRareData
.set(vm
, transition
, structure
);
400 void clearPreviousID()
402 if (typeInfo().structureHasRareData())
403 rareData()->clearPreviousID();
405 m_previousOrRareData
.clear();
408 int transitionCount() const
410 // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
411 return numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
);
414 bool isValid(JSGlobalObject
*, StructureChain
* cachedPrototypeChain
) const;
415 bool isValid(ExecState
*, StructureChain
* cachedPrototypeChain
) const;
419 Structure
* previous() const
421 ASSERT(!typeInfo().structureHasRareData());
422 return static_cast<Structure
*>(m_previousOrRareData
.get());
425 StructureRareData
* rareData() const
427 ASSERT(typeInfo().structureHasRareData());
428 return static_cast<StructureRareData
*>(m_previousOrRareData
.get());
431 bool checkOffsetConsistency() const;
433 void allocateRareData(VM
&);
434 void cloneRareDataFrom(VM
&, const Structure
*);
436 static const int s_maxTransitionLength
= 64;
438 static const unsigned maxSpecificFunctionThrashCount
= 3;
440 WriteBarrier
<JSGlobalObject
> m_globalObject
;
441 WriteBarrier
<Unknown
> m_prototype
;
442 mutable WriteBarrier
<StructureChain
> m_cachedPrototypeChain
;
444 WriteBarrier
<JSCell
> m_previousOrRareData
;
446 RefPtr
<StringImpl
> m_nameInPrevious
;
447 WriteBarrier
<JSCell
> m_specificValueInPrevious
;
449 const ClassInfo
* m_classInfo
;
451 StructureTransitionTable m_transitionTable
;
453 // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread.
454 WriteBarrier
<PropertyTable
> m_propertyTableUnsafe
;
456 mutable InlineWatchpointSet m_transitionWatchpointSet
;
458 COMPILE_ASSERT(firstOutOfLineOffset
< 256, firstOutOfLineOffset_fits
);
460 // m_offset does not account for anonymous slots
461 PropertyOffset m_offset
;
464 IndexingType m_indexingType
;
466 uint8_t m_inlineCapacity
;
467 unsigned m_dictionaryKind
: 2;
468 bool m_isPinnedPropertyTable
: 1;
469 bool m_hasGetterSetterProperties
: 1;
470 bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
: 1;
471 bool m_hasNonEnumerableProperties
: 1;
472 unsigned m_attributesInPrevious
: 14;
473 unsigned m_specificFunctionThrashCount
: 2;
474 unsigned m_preventExtensions
: 1;
475 unsigned m_didTransition
: 1;
476 unsigned m_staticFunctionReified
;
481 #endif // Structure_h