2 * Copyright (C) 2008, 2009 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 "Identifier.h"
34 #include "PropertyMapHashTable.h"
35 #include "PropertyNameArray.h"
37 #include "StructureTransitionTable.h"
38 #include "JSTypeInfo.h"
41 #include <wtf/PassOwnPtr.h>
42 #include <wtf/PassRefPtr.h>
43 #include <wtf/RefCounted.h>
48 class LLIntOffsetsExtractor
;
49 class PropertyNameArray
;
50 class PropertyNameArrayData
;
55 class Structure
: public JSCell
{
57 friend class StructureTransitionTable
;
61 static Structure
* create(JSGlobalData
& globalData
, JSGlobalObject
* globalObject
, JSValue prototype
, const TypeInfo
& typeInfo
, const ClassInfo
* classInfo
)
63 ASSERT(globalData
.structureStructure
);
65 Structure
* structure
= new (NotNull
, allocateCell
<Structure
>(globalData
.heap
)) Structure(globalData
, globalObject
, prototype
, typeInfo
, classInfo
);
66 structure
->finishCreation(globalData
);
71 void finishCreation(JSGlobalData
& globalData
)
73 Base::finishCreation(globalData
);
75 ASSERT(m_prototype
.isObject() || m_prototype
.isNull());
78 void finishCreation(JSGlobalData
& globalData
, CreatingEarlyCellTag
)
80 Base::finishCreation(globalData
, this, CreatingEarlyCell
);
82 ASSERT(m_prototype
.isNull());
83 ASSERT(!globalData
.structureStructure
);
87 static void dumpStatistics();
89 JS_EXPORT_PRIVATE
static Structure
* addPropertyTransition(JSGlobalData
&, Structure
*, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
, size_t& offset
);
90 JS_EXPORT_PRIVATE
static Structure
* addPropertyTransitionToExistingStructure(Structure
*, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
, size_t& offset
);
91 static Structure
* removePropertyTransition(JSGlobalData
&, Structure
*, const Identifier
& propertyName
, size_t& offset
);
92 JS_EXPORT_PRIVATE
static Structure
* changePrototypeTransition(JSGlobalData
&, Structure
*, JSValue prototype
);
93 JS_EXPORT_PRIVATE
static Structure
* despecifyFunctionTransition(JSGlobalData
&, Structure
*, const Identifier
&);
94 static Structure
* attributeChangeTransition(JSGlobalData
&, Structure
*, const Identifier
& propertyName
, unsigned attributes
);
95 static Structure
* toCacheableDictionaryTransition(JSGlobalData
&, Structure
*);
96 static Structure
* toUncacheableDictionaryTransition(JSGlobalData
&, Structure
*);
97 static Structure
* sealTransition(JSGlobalData
&, Structure
*);
98 static Structure
* freezeTransition(JSGlobalData
&, Structure
*);
99 static Structure
* preventExtensionsTransition(JSGlobalData
&, Structure
*);
101 bool isSealed(JSGlobalData
&);
102 bool isFrozen(JSGlobalData
&);
103 bool isExtensible() const { return !m_preventExtensions
; }
104 bool didTransition() const { return m_didTransition
; }
105 bool shouldGrowPropertyStorage() { return propertyStorageCapacity() == propertyStorageSize(); }
106 JS_EXPORT_PRIVATE
size_t suggestedNewPropertyStorageSize();
108 Structure
* flattenDictionaryStructure(JSGlobalData
&, JSObject
*);
110 static void destroy(JSCell
*);
112 // These should be used with caution.
113 JS_EXPORT_PRIVATE
size_t addPropertyWithoutTransition(JSGlobalData
&, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
);
114 size_t removePropertyWithoutTransition(JSGlobalData
&, const Identifier
& propertyName
);
115 void setPrototypeWithoutTransition(JSGlobalData
& globalData
, JSValue prototype
) { m_prototype
.set(globalData
, this, prototype
); }
117 bool isDictionary() const { return m_dictionaryKind
!= NoneDictionaryKind
; }
118 bool isUncacheableDictionary() const { return m_dictionaryKind
== UncachedDictionaryKind
; }
121 const TypeInfo
& typeInfo() const { ASSERT(structure()->classInfo() == &s_info
); return m_typeInfo
; }
122 bool isObject() const { return typeInfo().isObject(); }
125 JSGlobalObject
* globalObject() const { return m_globalObject
.get(); }
126 void setGlobalObject(JSGlobalData
& globalData
, JSGlobalObject
* globalObject
) { m_globalObject
.set(globalData
, this, globalObject
); }
128 JSValue
storedPrototype() const { return m_prototype
.get(); }
129 JSValue
prototypeForLookup(ExecState
*) const;
130 StructureChain
* prototypeChain(ExecState
*) const;
131 static void visitChildren(JSCell
*, SlotVisitor
&);
133 Structure
* previousID() const { ASSERT(structure()->classInfo() == &s_info
); return m_previous
.get(); }
134 bool transitivelyTransitionedFrom(Structure
* structureToFind
);
136 void growPropertyStorageCapacity();
137 unsigned propertyStorageCapacity() const { ASSERT(structure()->classInfo() == &s_info
); return m_propertyStorageCapacity
; }
138 unsigned propertyStorageSize() const { ASSERT(structure()->classInfo() == &s_info
); return (m_propertyTable
? m_propertyTable
->propertyStorageSize() : static_cast<unsigned>(m_offset
+ 1)); }
139 bool isUsingInlineStorage() const;
141 size_t get(JSGlobalData
&, const Identifier
& propertyName
);
142 size_t get(JSGlobalData
&, const UString
& name
);
143 JS_EXPORT_PRIVATE
size_t get(JSGlobalData
&, StringImpl
* propertyName
, unsigned& attributes
, JSCell
*& specificValue
);
144 size_t get(JSGlobalData
& globalData
, const Identifier
& propertyName
, unsigned& attributes
, JSCell
*& specificValue
)
146 ASSERT(!propertyName
.isNull());
147 ASSERT(structure()->classInfo() == &s_info
);
148 return get(globalData
, propertyName
.impl(), attributes
, specificValue
);
151 bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties
; }
152 bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
; }
153 void setHasGetterSetterProperties(bool is__proto__
)
155 m_hasGetterSetterProperties
= true;
157 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
= true;
159 void setContainsReadOnlyProperties()
161 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
= true;
164 bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties
; }
166 bool isEmpty() const { return m_propertyTable
? m_propertyTable
->isEmpty() : m_offset
== noOffset
; }
168 JS_EXPORT_PRIVATE
void despecifyDictionaryFunction(JSGlobalData
&, const Identifier
& propertyName
);
169 void disableSpecificFunctionTracking() { m_specificFunctionThrashCount
= maxSpecificFunctionThrashCount
; }
171 void setEnumerationCache(JSGlobalData
&, JSPropertyNameIterator
* enumerationCache
); // Defined in JSPropertyNameIterator.h.
172 JSPropertyNameIterator
* enumerationCache(); // Defined in JSPropertyNameIterator.h.
173 void getPropertyNamesFromStructure(JSGlobalData
&, PropertyNameArray
&, EnumerationMode
);
175 JSString
* objectToStringValue() { return m_objectToStringValue
.get(); }
177 void setObjectToStringValue(JSGlobalData
& globalData
, const JSCell
* owner
, JSString
* value
)
179 m_objectToStringValue
.set(globalData
, owner
, value
);
182 bool staticFunctionsReified()
184 return m_staticFunctionReified
;
187 void setStaticFunctionsReified()
189 m_staticFunctionReified
= true;
192 const ClassInfo
* classInfo() const { return m_classInfo
; }
194 static ptrdiff_t prototypeOffset()
196 return OBJECT_OFFSETOF(Structure
, m_prototype
);
199 static ptrdiff_t typeInfoFlagsOffset()
201 return OBJECT_OFFSETOF(Structure
, m_typeInfo
) + TypeInfo::flagsOffset();
204 static ptrdiff_t typeInfoTypeOffset()
206 return OBJECT_OFFSETOF(Structure
, m_typeInfo
) + TypeInfo::typeOffset();
209 static Structure
* createStructure(JSGlobalData
& globalData
)
211 ASSERT(!globalData
.structureStructure
);
212 Structure
* structure
= new (NotNull
, allocateCell
<Structure
>(globalData
.heap
)) Structure(globalData
);
213 structure
->finishCreation(globalData
, CreatingEarlyCell
);
217 static JS_EXPORTDATA
const ClassInfo s_info
;
220 friend class LLIntOffsetsExtractor
;
222 JS_EXPORT_PRIVATE
Structure(JSGlobalData
&, JSGlobalObject
*, JSValue prototype
, const TypeInfo
&, const ClassInfo
*);
223 Structure(JSGlobalData
&);
224 Structure(JSGlobalData
&, const Structure
*);
226 static Structure
* create(JSGlobalData
& globalData
, const Structure
* structure
)
228 ASSERT(globalData
.structureStructure
);
229 Structure
* newStructure
= new (NotNull
, allocateCell
<Structure
>(globalData
.heap
)) Structure(globalData
, structure
);
230 newStructure
->finishCreation(globalData
);
235 NoneDictionaryKind
= 0,
236 CachedDictionaryKind
= 1,
237 UncachedDictionaryKind
= 2
239 static Structure
* toDictionaryTransition(JSGlobalData
&, Structure
*, DictionaryKind
);
241 size_t putSpecificValue(JSGlobalData
&, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
);
242 size_t remove(const Identifier
& propertyName
);
244 void createPropertyMap(unsigned keyCount
= 0);
245 void checkConsistency();
247 bool despecifyFunction(JSGlobalData
&, const Identifier
&);
248 void despecifyAllFunctions(JSGlobalData
&);
250 PassOwnPtr
<PropertyTable
> copyPropertyTable(JSGlobalData
&, Structure
* owner
);
251 PassOwnPtr
<PropertyTable
> copyPropertyTableForPinning(JSGlobalData
&, Structure
* owner
);
252 JS_EXPORT_PRIVATE
void materializePropertyMap(JSGlobalData
&);
253 void materializePropertyMapIfNecessary(JSGlobalData
& globalData
)
255 ASSERT(structure()->classInfo() == &s_info
);
256 if (!m_propertyTable
&& m_previous
)
257 materializePropertyMap(globalData
);
259 void materializePropertyMapIfNecessaryForPinning(JSGlobalData
& globalData
)
261 ASSERT(structure()->classInfo() == &s_info
);
262 if (!m_propertyTable
)
263 materializePropertyMap(globalData
);
266 int transitionCount() const
268 // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
269 return m_offset
== noOffset
? 0 : m_offset
+ 1;
272 bool isValid(ExecState
*, StructureChain
* cachedPrototypeChain
) const;
276 static const int s_maxTransitionLength
= 64;
278 static const int noOffset
= -1;
280 static const unsigned maxSpecificFunctionThrashCount
= 3;
284 WriteBarrier
<JSGlobalObject
> m_globalObject
;
285 WriteBarrier
<Unknown
> m_prototype
;
286 mutable WriteBarrier
<StructureChain
> m_cachedPrototypeChain
;
288 WriteBarrier
<Structure
> m_previous
;
289 RefPtr
<StringImpl
> m_nameInPrevious
;
290 WriteBarrier
<JSCell
> m_specificValueInPrevious
;
292 const ClassInfo
* m_classInfo
;
294 StructureTransitionTable m_transitionTable
;
296 WriteBarrier
<JSPropertyNameIterator
> m_enumerationCache
;
298 OwnPtr
<PropertyTable
> m_propertyTable
;
300 uint32_t m_propertyStorageCapacity
;
302 WriteBarrier
<JSString
> m_objectToStringValue
;
304 // m_offset does not account for anonymous slots
307 unsigned m_dictionaryKind
: 2;
308 bool m_isPinnedPropertyTable
: 1;
309 bool m_hasGetterSetterProperties
: 1;
310 bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
: 1;
311 bool m_hasNonEnumerableProperties
: 1;
312 unsigned m_attributesInPrevious
: 7;
313 unsigned m_specificFunctionThrashCount
: 2;
314 unsigned m_preventExtensions
: 1;
315 unsigned m_didTransition
: 1;
316 unsigned m_staticFunctionReified
;
319 inline size_t Structure::get(JSGlobalData
& globalData
, const Identifier
& propertyName
)
321 ASSERT(structure()->classInfo() == &s_info
);
322 materializePropertyMapIfNecessary(globalData
);
323 if (!m_propertyTable
)
326 PropertyMapEntry
* entry
= m_propertyTable
->find(propertyName
.impl()).first
;
327 return entry
? entry
->offset
: notFound
;
330 inline size_t Structure::get(JSGlobalData
& globalData
, const UString
& name
)
332 ASSERT(structure()->classInfo() == &s_info
);
333 materializePropertyMapIfNecessary(globalData
);
334 if (!m_propertyTable
)
337 PropertyMapEntry
* entry
= m_propertyTable
->findWithString(name
.impl()).first
;
338 return entry
? entry
->offset
: notFound
;
341 inline bool JSCell::isObject() const
343 return m_structure
->isObject();
346 inline bool JSCell::isString() const
348 return m_structure
->typeInfo().type() == StringType
;
351 inline bool JSCell::isGetterSetter() const
353 return m_structure
->typeInfo().type() == GetterSetterType
;
356 inline bool JSCell::isAPIValueWrapper() const
358 return m_structure
->typeInfo().type() == APIValueWrapperType
;
361 inline void JSCell::setStructure(JSGlobalData
& globalData
, Structure
* structure
)
363 ASSERT(structure
->typeInfo().overridesVisitChildren() == this->structure()->typeInfo().overridesVisitChildren());
364 ASSERT(structure
->classInfo() == m_structure
->classInfo());
365 m_structure
.set(globalData
, this, structure
);
368 inline const ClassInfo
* JSCell::validatedClassInfo() const
370 #if ENABLE(GC_VALIDATION)
371 ASSERT(m_structure
.unvalidatedGet()->classInfo() == m_classInfo
);
373 ASSERT(m_structure
->classInfo() == m_classInfo
);
378 ALWAYS_INLINE
void MarkStack::internalAppend(JSCell
* cell
)
380 ASSERT(!m_isCheckingForDefaultMarkViolation
);
381 #if ENABLE(GC_VALIDATION)
385 if (Heap::testAndSetMarked(cell
) || !cell
->structure())
388 // Should never attempt to mark something that is zapped.
389 ASSERT(!cell
->isZapped());
391 m_stack
.append(cell
);
394 inline StructureTransitionTable::Hash::Key
StructureTransitionTable::keyForWeakGCMapFinalizer(void*, Structure
* structure
)
396 // Newer versions of the STL have an std::make_pair function that takes rvalue references.
397 // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue.
398 // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details.
399 return Hash::Key(structure
->m_nameInPrevious
.get(), +structure
->m_attributesInPrevious
);
402 inline bool Structure::transitivelyTransitionedFrom(Structure
* structureToFind
)
404 for (Structure
* current
= this; current
; current
= current
->previousID()) {
405 if (current
== structureToFind
)
411 inline JSCell::JSCell(JSGlobalData
& globalData
, Structure
* structure
)
412 : m_classInfo(structure
->classInfo())
413 , m_structure(globalData
, this, structure
)
417 inline void JSCell::finishCreation(JSGlobalData
& globalData
, Structure
* structure
, CreatingEarlyCellTag
)
419 #if ENABLE(GC_VALIDATION)
420 ASSERT(globalData
.isInitializingObject());
421 globalData
.setInitializingObjectClass(0);
424 m_structure
.setEarlyValue(globalData
, this, structure
);
425 m_classInfo
= structure
->classInfo();
426 // Very first set of allocations won't have a real structure.
427 ASSERT(m_structure
|| !globalData
.structureStructure
);
432 #endif // Structure_h