]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
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. | |
12 | * | |
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. | |
24 | */ | |
25 | ||
26 | #ifndef Structure_h | |
27 | #define Structure_h | |
28 | ||
29 | #include "ClassInfo.h" | |
30 | #include "Identifier.h" | |
31 | #include "JSCell.h" | |
32 | #include "JSType.h" | |
33 | #include "JSValue.h" | |
34 | #include "PropertyMapHashTable.h" | |
35 | #include "PropertyNameArray.h" | |
36 | #include "Protect.h" | |
37 | #include "StructureTransitionTable.h" | |
38 | #include "JSTypeInfo.h" | |
39 | #include "UString.h" | |
40 | #include "Weak.h" | |
41 | #include <wtf/PassOwnPtr.h> | |
42 | #include <wtf/PassRefPtr.h> | |
43 | #include <wtf/RefCounted.h> | |
44 | ||
45 | ||
46 | namespace JSC { | |
47 | ||
48 | class LLIntOffsetsExtractor; | |
49 | class PropertyNameArray; | |
50 | class PropertyNameArrayData; | |
51 | class StructureChain; | |
52 | class SlotVisitor; | |
53 | class JSString; | |
54 | ||
55 | class Structure : public JSCell { | |
56 | public: | |
57 | friend class StructureTransitionTable; | |
58 | ||
59 | typedef JSCell Base; | |
60 | ||
61 | static Structure* create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo) | |
62 | { | |
63 | ASSERT(globalData.structureStructure); | |
64 | ASSERT(classInfo); | |
65 | Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo); | |
66 | structure->finishCreation(globalData); | |
67 | return structure; | |
68 | } | |
69 | ||
70 | protected: | |
71 | void finishCreation(JSGlobalData& globalData) | |
72 | { | |
73 | Base::finishCreation(globalData); | |
74 | ASSERT(m_prototype); | |
75 | ASSERT(m_prototype.isObject() || m_prototype.isNull()); | |
76 | } | |
77 | ||
78 | void finishCreation(JSGlobalData& globalData, CreatingEarlyCellTag) | |
79 | { | |
80 | Base::finishCreation(globalData, this, CreatingEarlyCell); | |
81 | ASSERT(m_prototype); | |
82 | ASSERT(m_prototype.isNull()); | |
83 | ASSERT(!globalData.structureStructure); | |
84 | } | |
85 | ||
86 | public: | |
87 | static void dumpStatistics(); | |
88 | ||
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*); | |
100 | ||
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(); | |
107 | ||
108 | Structure* flattenDictionaryStructure(JSGlobalData&, JSObject*); | |
109 | ||
110 | static void destroy(JSCell*); | |
111 | ||
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); } | |
116 | ||
117 | bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; } | |
118 | bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; } | |
119 | ||
120 | // Type accessors. | |
121 | const TypeInfo& typeInfo() const { ASSERT(structure()->classInfo() == &s_info); return m_typeInfo; } | |
122 | bool isObject() const { return typeInfo().isObject(); } | |
123 | ||
124 | ||
125 | JSGlobalObject* globalObject() const { return m_globalObject.get(); } | |
126 | void setGlobalObject(JSGlobalData& globalData, JSGlobalObject* globalObject) { m_globalObject.set(globalData, this, globalObject); } | |
127 | ||
128 | JSValue storedPrototype() const { return m_prototype.get(); } | |
129 | JSValue prototypeForLookup(ExecState*) const; | |
130 | StructureChain* prototypeChain(ExecState*) const; | |
131 | static void visitChildren(JSCell*, SlotVisitor&); | |
132 | ||
133 | Structure* previousID() const { ASSERT(structure()->classInfo() == &s_info); return m_previous.get(); } | |
134 | bool transitivelyTransitionedFrom(Structure* structureToFind); | |
135 | ||
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; | |
140 | ||
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) | |
145 | { | |
146 | ASSERT(!propertyName.isNull()); | |
147 | ASSERT(structure()->classInfo() == &s_info); | |
148 | return get(globalData, propertyName.impl(), attributes, specificValue); | |
149 | } | |
150 | ||
151 | bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; } | |
152 | bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto; } | |
153 | void setHasGetterSetterProperties(bool is__proto__) | |
154 | { | |
155 | m_hasGetterSetterProperties = true; | |
156 | if (!is__proto__) | |
157 | m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true; | |
158 | } | |
159 | void setContainsReadOnlyProperties() | |
160 | { | |
161 | m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true; | |
162 | } | |
163 | ||
164 | bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; } | |
165 | ||
166 | bool isEmpty() const { return m_propertyTable ? m_propertyTable->isEmpty() : m_offset == noOffset; } | |
167 | ||
168 | JS_EXPORT_PRIVATE void despecifyDictionaryFunction(JSGlobalData&, const Identifier& propertyName); | |
169 | void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; } | |
170 | ||
171 | void setEnumerationCache(JSGlobalData&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h. | |
172 | JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h. | |
173 | void getPropertyNamesFromStructure(JSGlobalData&, PropertyNameArray&, EnumerationMode); | |
174 | ||
175 | JSString* objectToStringValue() { return m_objectToStringValue.get(); } | |
176 | ||
177 | void setObjectToStringValue(JSGlobalData& globalData, const JSCell* owner, JSString* value) | |
178 | { | |
179 | m_objectToStringValue.set(globalData, owner, value); | |
180 | } | |
181 | ||
182 | bool staticFunctionsReified() | |
183 | { | |
184 | return m_staticFunctionReified; | |
185 | } | |
186 | ||
187 | void setStaticFunctionsReified() | |
188 | { | |
189 | m_staticFunctionReified = true; | |
190 | } | |
191 | ||
192 | const ClassInfo* classInfo() const { return m_classInfo; } | |
193 | ||
194 | static ptrdiff_t prototypeOffset() | |
195 | { | |
196 | return OBJECT_OFFSETOF(Structure, m_prototype); | |
197 | } | |
198 | ||
199 | static ptrdiff_t typeInfoFlagsOffset() | |
200 | { | |
201 | return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::flagsOffset(); | |
202 | } | |
203 | ||
204 | static ptrdiff_t typeInfoTypeOffset() | |
205 | { | |
206 | return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::typeOffset(); | |
207 | } | |
208 | ||
209 | static Structure* createStructure(JSGlobalData& globalData) | |
210 | { | |
211 | ASSERT(!globalData.structureStructure); | |
212 | Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData); | |
213 | structure->finishCreation(globalData, CreatingEarlyCell); | |
214 | return structure; | |
215 | } | |
216 | ||
217 | static JS_EXPORTDATA const ClassInfo s_info; | |
218 | ||
219 | private: | |
220 | friend class LLIntOffsetsExtractor; | |
221 | ||
222 | JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*); | |
223 | Structure(JSGlobalData&); | |
224 | Structure(JSGlobalData&, const Structure*); | |
225 | ||
226 | static Structure* create(JSGlobalData& globalData, const Structure* structure) | |
227 | { | |
228 | ASSERT(globalData.structureStructure); | |
229 | Structure* newStructure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, structure); | |
230 | newStructure->finishCreation(globalData); | |
231 | return newStructure; | |
232 | } | |
233 | ||
234 | typedef enum { | |
235 | NoneDictionaryKind = 0, | |
236 | CachedDictionaryKind = 1, | |
237 | UncachedDictionaryKind = 2 | |
238 | } DictionaryKind; | |
239 | static Structure* toDictionaryTransition(JSGlobalData&, Structure*, DictionaryKind); | |
240 | ||
241 | size_t putSpecificValue(JSGlobalData&, const Identifier& propertyName, unsigned attributes, JSCell* specificValue); | |
242 | size_t remove(const Identifier& propertyName); | |
243 | ||
244 | void createPropertyMap(unsigned keyCount = 0); | |
245 | void checkConsistency(); | |
246 | ||
247 | bool despecifyFunction(JSGlobalData&, const Identifier&); | |
248 | void despecifyAllFunctions(JSGlobalData&); | |
249 | ||
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) | |
254 | { | |
255 | ASSERT(structure()->classInfo() == &s_info); | |
256 | if (!m_propertyTable && m_previous) | |
257 | materializePropertyMap(globalData); | |
258 | } | |
259 | void materializePropertyMapIfNecessaryForPinning(JSGlobalData& globalData) | |
260 | { | |
261 | ASSERT(structure()->classInfo() == &s_info); | |
262 | if (!m_propertyTable) | |
263 | materializePropertyMap(globalData); | |
264 | } | |
265 | ||
266 | int transitionCount() const | |
267 | { | |
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; | |
270 | } | |
271 | ||
272 | bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; | |
273 | ||
274 | void pin(); | |
275 | ||
276 | static const int s_maxTransitionLength = 64; | |
277 | ||
278 | static const int noOffset = -1; | |
279 | ||
280 | static const unsigned maxSpecificFunctionThrashCount = 3; | |
281 | ||
282 | TypeInfo m_typeInfo; | |
283 | ||
284 | WriteBarrier<JSGlobalObject> m_globalObject; | |
285 | WriteBarrier<Unknown> m_prototype; | |
286 | mutable WriteBarrier<StructureChain> m_cachedPrototypeChain; | |
287 | ||
288 | WriteBarrier<Structure> m_previous; | |
289 | RefPtr<StringImpl> m_nameInPrevious; | |
290 | WriteBarrier<JSCell> m_specificValueInPrevious; | |
291 | ||
292 | const ClassInfo* m_classInfo; | |
293 | ||
294 | StructureTransitionTable m_transitionTable; | |
295 | ||
296 | WriteBarrier<JSPropertyNameIterator> m_enumerationCache; | |
297 | ||
298 | OwnPtr<PropertyTable> m_propertyTable; | |
299 | ||
300 | uint32_t m_propertyStorageCapacity; | |
301 | ||
302 | WriteBarrier<JSString> m_objectToStringValue; | |
303 | ||
304 | // m_offset does not account for anonymous slots | |
305 | int m_offset; | |
306 | ||
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; | |
317 | }; | |
318 | ||
319 | inline size_t Structure::get(JSGlobalData& globalData, const Identifier& propertyName) | |
320 | { | |
321 | ASSERT(structure()->classInfo() == &s_info); | |
322 | materializePropertyMapIfNecessary(globalData); | |
323 | if (!m_propertyTable) | |
324 | return notFound; | |
325 | ||
326 | PropertyMapEntry* entry = m_propertyTable->find(propertyName.impl()).first; | |
327 | return entry ? entry->offset : notFound; | |
328 | } | |
329 | ||
330 | inline size_t Structure::get(JSGlobalData& globalData, const UString& name) | |
331 | { | |
332 | ASSERT(structure()->classInfo() == &s_info); | |
333 | materializePropertyMapIfNecessary(globalData); | |
334 | if (!m_propertyTable) | |
335 | return notFound; | |
336 | ||
337 | PropertyMapEntry* entry = m_propertyTable->findWithString(name.impl()).first; | |
338 | return entry ? entry->offset : notFound; | |
339 | } | |
340 | ||
341 | inline bool JSCell::isObject() const | |
342 | { | |
343 | return m_structure->isObject(); | |
344 | } | |
345 | ||
346 | inline bool JSCell::isString() const | |
347 | { | |
348 | return m_structure->typeInfo().type() == StringType; | |
349 | } | |
350 | ||
351 | inline bool JSCell::isGetterSetter() const | |
352 | { | |
353 | return m_structure->typeInfo().type() == GetterSetterType; | |
354 | } | |
355 | ||
356 | inline bool JSCell::isAPIValueWrapper() const | |
357 | { | |
358 | return m_structure->typeInfo().type() == APIValueWrapperType; | |
359 | } | |
360 | ||
361 | inline void JSCell::setStructure(JSGlobalData& globalData, Structure* structure) | |
362 | { | |
363 | ASSERT(structure->typeInfo().overridesVisitChildren() == this->structure()->typeInfo().overridesVisitChildren()); | |
364 | ASSERT(structure->classInfo() == m_structure->classInfo()); | |
365 | m_structure.set(globalData, this, structure); | |
366 | } | |
367 | ||
368 | inline const ClassInfo* JSCell::validatedClassInfo() const | |
369 | { | |
370 | #if ENABLE(GC_VALIDATION) | |
371 | ASSERT(m_structure.unvalidatedGet()->classInfo() == m_classInfo); | |
372 | #else | |
373 | ASSERT(m_structure->classInfo() == m_classInfo); | |
374 | #endif | |
375 | return m_classInfo; | |
376 | } | |
377 | ||
378 | ALWAYS_INLINE void MarkStack::internalAppend(JSCell* cell) | |
379 | { | |
380 | ASSERT(!m_isCheckingForDefaultMarkViolation); | |
381 | #if ENABLE(GC_VALIDATION) | |
382 | validate(cell); | |
383 | #endif | |
384 | m_visitCount++; | |
385 | if (Heap::testAndSetMarked(cell) || !cell->structure()) | |
386 | return; | |
387 | ||
388 | // Should never attempt to mark something that is zapped. | |
389 | ASSERT(!cell->isZapped()); | |
390 | ||
391 | m_stack.append(cell); | |
392 | } | |
393 | ||
394 | inline StructureTransitionTable::Hash::Key StructureTransitionTable::keyForWeakGCMapFinalizer(void*, Structure* structure) | |
395 | { | |
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); | |
400 | } | |
401 | ||
402 | inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind) | |
403 | { | |
404 | for (Structure* current = this; current; current = current->previousID()) { | |
405 | if (current == structureToFind) | |
406 | return true; | |
407 | } | |
408 | return false; | |
409 | } | |
410 | ||
411 | inline JSCell::JSCell(JSGlobalData& globalData, Structure* structure) | |
412 | : m_classInfo(structure->classInfo()) | |
413 | , m_structure(globalData, this, structure) | |
414 | { | |
415 | } | |
416 | ||
417 | inline void JSCell::finishCreation(JSGlobalData& globalData, Structure* structure, CreatingEarlyCellTag) | |
418 | { | |
419 | #if ENABLE(GC_VALIDATION) | |
420 | ASSERT(globalData.isInitializingObject()); | |
421 | globalData.setInitializingObjectClass(0); | |
422 | if (structure) | |
423 | #endif | |
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); | |
428 | } | |
429 | ||
430 | } // namespace JSC | |
431 | ||
432 | #endif // Structure_h |