Remove static tables for bindings that use eager reification
[WebKit-https.git] / Source / JavaScriptCore / 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, const JSCell* owner, JSString* value)
319     {
320         if (!m_hasRareData)
321             allocateRareData(vm);
322         rareData()->setObjectToStringValue(vm, owner, 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&, Structure* owner);
433     PropertyTable* copyPropertyTable(VM&, Structure* owner);
434     PropertyTable* copyPropertyTableForPinning(VM&, Structure* owner);
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* transition, Structure* structure)
465     {
466         if (m_hasRareData)
467             rareData()->setPreviousID(vm, transition, structure);
468         else
469             m_previousOrRareData.set(vm, transition, 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