]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/Structure.h
JavaScriptCore-7600.1.4.15.12.tar.gz
[apple/javascriptcore.git] / runtime / Structure.h
1 /*
2 * Copyright (C) 2008, 2009, 2012, 2013 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 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.
24 */
25
26 #ifndef Structure_h
27 #define Structure_h
28
29 #include "ClassInfo.h"
30 #include "ConcurrentJITLock.h"
31 #include "IndexingType.h"
32 #include "JSCJSValue.h"
33 #include "JSCell.h"
34 #include "JSType.h"
35 #include "PropertyName.h"
36 #include "PropertyNameArray.h"
37 #include "PropertyOffset.h"
38 #include "Protect.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"
45 #include "Weak.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>
52
53
54 namespace JSC {
55
56 class DeferGC;
57 class LLIntOffsetsExtractor;
58 class PropertyNameArray;
59 class PropertyNameArrayData;
60 class PropertyTable;
61 class StructureChain;
62 class SlotVisitor;
63 class JSString;
64 struct DumpContext;
65
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;
70
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;
74
75 class Structure : public JSCell {
76 public:
77 friend class StructureTransitionTable;
78
79 typedef JSCell Base;
80
81 static Structure* create(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0);
82
83 ~Structure();
84
85 protected:
86 void finishCreation(VM& vm)
87 {
88 Base::finishCreation(vm);
89 ASSERT(m_prototype);
90 ASSERT(m_prototype.isObject() || m_prototype.isNull());
91 }
92
93 void finishCreation(VM& vm, CreatingEarlyCellTag)
94 {
95 Base::finishCreation(vm, this, CreatingEarlyCell);
96 ASSERT(m_prototype);
97 ASSERT(m_prototype.isNull());
98 ASSERT(!vm.structureStructure);
99 }
100
101 public:
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(); }
105
106 bool isProxy() const
107 {
108 JSType type = m_blob.type();
109 return type == ImpureProxyType || type == PureForwardingProxyType;
110 }
111
112 static void dumpStatistics();
113
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);
127
128 bool isSealed(VM&);
129 bool isFrozen(VM&);
130 bool isExtensible() const { return !m_preventExtensions; }
131 bool didTransition() const { return m_didTransition; }
132 bool putWillGrowOutOfLineStorage();
133 size_t suggestedNewOutOfLineStorageCapacity();
134
135 JS_EXPORT_PRIVATE Structure* flattenDictionaryStructure(VM&, JSObject*);
136
137 static const bool needsDestruction = true;
138 static const bool hasImmortalStructure = true;
139 static void destroy(JSCell*);
140
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); }
145
146 bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; }
147 bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; }
148
149 bool hasBeenFlattenedBefore() const { return m_hasBeenFlattenedBefore; }
150
151 bool propertyAccessesAreCacheable() { return m_dictionaryKind != UncachedDictionaryKind && !typeInfo().prohibitsPropertyCaching(); }
152
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()
156 {
157 return typeInfo().hasImpureGetOwnPropertySlot();
158 }
159
160 // Type accessors.
161 TypeInfo typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_blob.typeInfo(m_outOfLineTypeFlags); }
162 bool isObject() const { return typeInfo().isObject(); }
163
164 IndexingType indexingType() const { return m_blob.indexingType() & AllArrayTypes; }
165 IndexingType indexingTypeIncludingHistory() const { return m_blob.indexingType(); }
166
167 bool mayInterceptIndexedAccesses() const
168 {
169 return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors);
170 }
171
172 bool anyObjectInChainMayInterceptIndexedAccesses() const;
173 bool holesMustForwardToPrototype(VM&) const;
174
175 bool needsSlowPutIndexing() const;
176 NonPropertyTransition suggestedArrayStorageTransition() const;
177
178 JSGlobalObject* globalObject() const { return m_globalObject.get(); }
179 void setGlobalObject(VM& vm, JSGlobalObject* globalObject) { m_globalObject.set(vm, this, globalObject); }
180
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&);
190
191 // Will just the prototype chain intercept this property access?
192 bool prototypeChainMayInterceptStoreTo(VM&, PropertyName);
193
194 bool transitionDidInvolveSpecificValue() const { return !!m_specificValueInPrevious; }
195
196 Structure* previousID() const
197 {
198 ASSERT(structure()->classInfo() == info());
199 if (m_hasRareData)
200 return rareData()->previousID();
201 return previous();
202 }
203 bool transitivelyTransitionedFrom(Structure* structureToFind);
204
205 unsigned outOfLineCapacity() const
206 {
207 ASSERT(checkOffsetConsistency());
208
209 unsigned outOfLineSize = this->outOfLineSize();
210
211 if (!outOfLineSize)
212 return 0;
213
214 if (outOfLineSize <= initialOutOfLineCapacity)
215 return initialOutOfLineCapacity;
216
217 ASSERT(outOfLineSize > initialOutOfLineCapacity);
218 COMPILE_ASSERT(outOfLineGrowthFactor == 2, outOfLineGrowthFactor_is_two);
219 return WTF::roundUpToPowerOfTwo(outOfLineSize);
220 }
221 unsigned outOfLineSize() const
222 {
223 ASSERT(checkOffsetConsistency());
224 ASSERT(structure()->classInfo() == info());
225
226 return numberOfOutOfLineSlotsForLastOffset(m_offset);
227 }
228 bool hasInlineStorage() const
229 {
230 return !!m_inlineCapacity;
231 }
232 unsigned inlineCapacity() const
233 {
234 return m_inlineCapacity;
235 }
236 unsigned inlineSize() const
237 {
238 return std::min<unsigned>(m_offset + 1, m_inlineCapacity);
239 }
240 unsigned totalStorageSize() const
241 {
242 return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
243 }
244 unsigned totalStorageCapacity() const
245 {
246 ASSERT(structure()->classInfo() == info());
247 return outOfLineCapacity() + inlineCapacity();
248 }
249
250 bool isValidOffset(PropertyOffset offset) const
251 {
252 return JSC::isValidOffset(offset)
253 && offset <= m_offset
254 && (offset < m_inlineCapacity || offset >= firstOutOfLineOffset);
255 }
256
257 bool couldHaveIndexingHeader() const
258 {
259 return hasIndexedProperties(indexingType())
260 || isTypedView(m_classInfo->typedArrayStorageType);
261 }
262
263 bool hasIndexingHeader(const JSCell*) const;
264
265 bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject);
266
267 PropertyOffset get(VM&, PropertyName);
268 PropertyOffset get(VM&, const WTF::String& name);
269 PropertyOffset get(VM&, PropertyName, unsigned& attributes, JSCell*& specificValue);
270
271 PropertyOffset getConcurrently(VM&, StringImpl* uid);
272 PropertyOffset getConcurrently(VM&, StringImpl* uid, unsigned& attributes, JSCell*& specificValue);
273
274 bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
275 bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto; }
276 void setHasGetterSetterProperties(bool is__proto__)
277 {
278 m_hasGetterSetterProperties = true;
279 if (!is__proto__)
280 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
281 }
282
283 bool hasCustomGetterSetterProperties() const { return m_hasCustomGetterSetterProperties; }
284 void setHasCustomGetterSetterProperties(bool is__proto__)
285 {
286 m_hasCustomGetterSetterProperties = true;
287 if (!is__proto__)
288 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
289 }
290
291 void setContainsReadOnlyProperties()
292 {
293 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
294 }
295
296 bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
297
298 bool isEmpty() const
299 {
300 ASSERT(checkOffsetConsistency());
301 return !JSC::isValidOffset(m_offset);
302 }
303
304 JS_EXPORT_PRIVATE void despecifyDictionaryFunction(VM&, PropertyName);
305 void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; }
306
307 void setEnumerationCache(VM&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h.
308 JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h.
309 void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
310
311 JSString* objectToStringValue()
312 {
313 if (!m_hasRareData)
314 return 0;
315 return rareData()->objectToStringValue();
316 }
317
318 void setObjectToStringValue(VM& vm, JSString* value)
319 {
320 if (!m_hasRareData)
321 allocateRareData(vm);
322 rareData()->setObjectToStringValue(vm, value);
323 }
324
325 bool staticFunctionsReified()
326 {
327 return m_staticFunctionReified;
328 }
329
330 void setStaticFunctionsReified()
331 {
332 m_staticFunctionReified = true;
333 }
334
335 const ClassInfo* classInfo() const { return m_classInfo; }
336
337 static ptrdiff_t structureIDOffset()
338 {
339 return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset();
340 }
341
342 static ptrdiff_t prototypeOffset()
343 {
344 return OBJECT_OFFSETOF(Structure, m_prototype);
345 }
346
347 static ptrdiff_t globalObjectOffset()
348 {
349 return OBJECT_OFFSETOF(Structure, m_globalObject);
350 }
351
352 static ptrdiff_t classInfoOffset()
353 {
354 return OBJECT_OFFSETOF(Structure, m_classInfo);
355 }
356
357 static ptrdiff_t indexingTypeOffset()
358 {
359 return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingTypeOffset();
360 }
361
362 static Structure* createStructure(VM&);
363
364 bool transitionWatchpointSetHasBeenInvalidated() const
365 {
366 return m_transitionWatchpointSet.hasBeenInvalidated();
367 }
368
369 bool transitionWatchpointSetIsStillValid() const
370 {
371 return m_transitionWatchpointSet.isStillValid();
372 }
373
374 void addTransitionWatchpoint(Watchpoint* watchpoint) const
375 {
376 ASSERT(transitionWatchpointSetIsStillValid());
377 m_transitionWatchpointSet.add(watchpoint);
378 }
379
380 void notifyTransitionFromThisStructure() const
381 {
382 m_transitionWatchpointSet.fireAll();
383 }
384
385 InlineWatchpointSet& transitionWatchpointSet() const
386 {
387 return m_transitionWatchpointSet;
388 }
389
390 void dump(PrintStream&) const;
391 void dumpInContext(PrintStream&, DumpContext*) const;
392 void dumpBrief(PrintStream&, const CString&) const;
393
394 static void dumpContextHeader(PrintStream&);
395
396 DECLARE_EXPORT_INFO;
397
398 private:
399 friend class LLIntOffsetsExtractor;
400
401 JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity);
402 Structure(VM&);
403 Structure(VM&, Structure*);
404
405 static Structure* create(VM&, Structure*);
406
407 static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset&);
408
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
412 // to unlock it.
413 void findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*&, PropertyTable*&);
414
415 typedef enum {
416 NoneDictionaryKind = 0,
417 CachedDictionaryKind = 1,
418 UncachedDictionaryKind = 2
419 } DictionaryKind;
420 static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind);
421
422 PropertyOffset putSpecificValue(VM&, PropertyName, unsigned attributes, JSCell* specificValue);
423 PropertyOffset remove(PropertyName);
424
425 void createPropertyMap(const GCSafeConcurrentJITLocker&, VM&, unsigned keyCount = 0);
426 void checkConsistency();
427
428 bool despecifyFunction(VM&, PropertyName);
429 void despecifyAllFunctions(VM&);
430
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&)
437 {
438 ASSERT(!isCompilationThread());
439 ASSERT(structure()->classInfo() == info());
440 ASSERT(checkOffsetConsistency());
441 if (!propertyTable() && previousID())
442 materializePropertyMap(vm);
443 }
444 ALWAYS_INLINE void materializePropertyMapIfNecessary(VM& vm, PropertyTable*& table)
445 {
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();
454 }
455 }
456 void materializePropertyMapIfNecessaryForPinning(VM& vm, DeferGC&)
457 {
458 ASSERT(structure()->classInfo() == info());
459 checkOffsetConsistency();
460 if (!propertyTable())
461 materializePropertyMap(vm);
462 }
463
464 void setPreviousID(VM& vm, Structure* structure)
465 {
466 if (m_hasRareData)
467 rareData()->setPreviousID(vm, structure);
468 else
469 m_previousOrRareData.set(vm, this, structure);
470 }
471
472 void clearPreviousID()
473 {
474 if (m_hasRareData)
475 rareData()->clearPreviousID();
476 else
477 m_previousOrRareData.clear();
478 }
479
480 int transitionCount() const
481 {
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);
484 }
485
486 bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const;
487 bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
488
489 void pin();
490
491 Structure* previous() const
492 {
493 ASSERT(!m_hasRareData);
494 return static_cast<Structure*>(m_previousOrRareData.get());
495 }
496
497 StructureRareData* rareData() const
498 {
499 ASSERT(m_hasRareData);
500 return static_cast<StructureRareData*>(m_previousOrRareData.get());
501 }
502
503 bool checkOffsetConsistency() const;
504
505 void allocateRareData(VM&);
506 void cloneRareDataFrom(VM&, const Structure*);
507
508 static const int s_maxTransitionLength = 64;
509 static const int s_maxTransitionLengthForNonEvalPutById = 512;
510
511 static const unsigned maxSpecificFunctionThrashCount = 3;
512
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;
517
518 WriteBarrier<JSGlobalObject> m_globalObject;
519 WriteBarrier<Unknown> m_prototype;
520 mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
521
522 WriteBarrier<JSCell> m_previousOrRareData;
523
524 RefPtr<StringImpl> m_nameInPrevious;
525 WriteBarrier<JSCell> m_specificValueInPrevious;
526
527 const ClassInfo* m_classInfo;
528
529 StructureTransitionTable m_transitionTable;
530
531 // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread.
532 WriteBarrier<PropertyTable> m_propertyTableUnsafe;
533
534 mutable InlineWatchpointSet m_transitionWatchpointSet;
535
536 COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits);
537
538 // m_offset does not account for anonymous slots
539 PropertyOffset m_offset;
540
541 uint8_t m_inlineCapacity;
542
543 ConcurrentJITLock m_lock;
544
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;
558 };
559
560 } // namespace JSC
561
562 #endif // Structure_h